Swift: 使用snapkit时如何获取正确的UICollectionView高度?
Swift: How to get correct UICollectionView height when using snapkit?
我可能会以完全错误的方式解决这个问题,因为我尝试做的应该很简单...我有一个包含几个子视图的视图组件,其中一个是 UICollectionView。我使用 SnapKit 布局子视图。 UICollectionView 是按钮的流式布局,跨越 0-n 行,按钮的数量从一开始就已知。
当我通过 SnapKit 配置约束时,集合的高度为 0,因为不存在任何项目(仍未调用 cellForItemAt)。
我将如何在 SnapKit 中动态设置集合的高度?或者是否有更好的方法来获得我正在寻找的非常基本的流程布局(动态高度调整)?
(注:代码略有简化,只显示相关部分)
class CategoryList: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate {
var categories : [String]?
var buttons = [BorderedButton]()
private(set) lazy var collectionView: UICollectionView = {
var flowLayout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: flowLayout)
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "collectionCell")
collectionView.delegate = self
collectionView.dataSource = self
collectionView.backgroundColor = UIColor.cyan
return collectionView
}()
override func loadView() {
super.loadView()
self.view.addSubview(collectionView)
}
override func viewDidLoad() {
super.viewDidLoad()
setupConstraints()
}
override func viewDidAppear(_ animated: Bool) {
print("123: \(collectionView.contentSize.height)")
//prints 47 for this particular example, however doing snp.makeConstraints
//(or remakeConstraints) from here doesn't make any difference
//setupConstraints()
}
func configure(categories:[String]) {
self.categories = categories
for category in categories {
let button = BorderedButton()
button.setTitle(category, for: .normal)
buttons.append(button)
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return buttons.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath)
cell.addSubview(buttons[indexPath.row])
//prints 47 for this particular example, however doing snp.makeConstraints
//(or remakeConstraints) from here doesn't make any difference
print("XYZ: \(collectionView.contentSize.height)")
return cell
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize
{
return buttons[indexPath.row].intrinsicContentSize
}
private func setupConstraints() {
collectionView.snp.makeConstraints { (make) in
make.left.top.equalToSuperview()
make.width.equalTo(300)
print("ABC: \(collectionView.contentSize.height)")
//this does not work since height=0
make.height.equalTo(collectionView.contentSize.height)
//this works fine
make.height.equalTo(47)
}
}
}
谢谢!
编辑:添加缺失的 class
感谢您在下方回复。它确实有效,即使我错过了一半的代码,你也能得到这个问题 :-)
下面是缺少的部分,它是使用 CategoryList 的 class。在您的帮助下,我让 CategoryList 动态扩展到正确的高度,这非常好。但是,它弄乱了周围视图(下面的 view1 和 view2)的位置。
我怀疑这与
有关
make.height.equalTo(categoryList.collectionView.snp.height)
行,也许它应该只引用 collectionView 约束 属性,但我不知道应该(甚至可以)怎么做?
class PlaylistDetailsHeaderView : UITableViewCell {
private var categoryList:CategoryList
private(set) lazy var view1: UIView = {
let view = UIView()
return view
}()
private(set) lazy var view2: UIView = {
let view = UIView()
return view
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
self.categoryList = CategoryList()
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(view1)
self.contentView.addSubview(view2)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure() {
let categories = ["alfa", "beta", "gamma", "fkdiii", "keoooe", "dfdsje", "jkjfldsjflsd", "alfa", "beta", "gamma", "fkdiii", "keoooe", "dfdsje", "jkjfldsjflsd"]
categoryList.configure(categories: categories)
self.contentView.addSubview(categoryList.view)
setupConstraints()
}
private func setupConstraints() {
view1.snp.makeConstraints { make in
make.left.top.equalToSuperview()
}
categoryList.view.snp.makeConstraints{ make in
make.left.equalToSuperview()
make.top.equalTo(view1.snp.bottom).offset(20)
make.width.equalTo(300)
make.height.equalTo(categoryList.collectionView.snp.height)
}
view2.snp.makeConstraints { make in
make.top.equalTo(categoryList.view.snp.bottom).offset(20)
}
}
}
如果我明白你的意思,我认为你应该这样做。
首先你需要在某处存储高度。这将默认为 0,或者在您的情况下为 47。
var heightConstraint: Constraint?
然后您将像往常一样在 snapkit 中设置约束,但是当您设置高度约束时,您将其存储在之前创建的变量中。不要忘记添加“.constraint”。
heightConstraint = make.height.equalTo(47).constraint
下一步是填充 collectionView 时,您可以再次使用 heightConstraint(如果您使变量可达)
heightConstraint.updateOffset(collectionView.contentSize.height)
Vollan 的答案非常接近。更新约束时,只需确保您在主线程上即可。所以总结一下:
var collectionViewHeightConstraint: Constraint? // on top as class property
然后在 snapkit 块内:
collectionView.snp.makeConstraints { make in
make.top.equalToSuperview() //example
collectionViewHeightConstraint = make.height.equalTo(self.collectionView.contentSize.height).constraint
}
最后但同样重要的是,当数据更新时,请不要忘记:
DispatchQueue.main.async {
self.collectionViewHeightConstraint?.update(offset: self.collectionView.contentSize.height)
}
我可能会以完全错误的方式解决这个问题,因为我尝试做的应该很简单...我有一个包含几个子视图的视图组件,其中一个是 UICollectionView。我使用 SnapKit 布局子视图。 UICollectionView 是按钮的流式布局,跨越 0-n 行,按钮的数量从一开始就已知。
当我通过 SnapKit 配置约束时,集合的高度为 0,因为不存在任何项目(仍未调用 cellForItemAt)。
我将如何在 SnapKit 中动态设置集合的高度?或者是否有更好的方法来获得我正在寻找的非常基本的流程布局(动态高度调整)?
(注:代码略有简化,只显示相关部分)
class CategoryList: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate {
var categories : [String]?
var buttons = [BorderedButton]()
private(set) lazy var collectionView: UICollectionView = {
var flowLayout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: flowLayout)
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "collectionCell")
collectionView.delegate = self
collectionView.dataSource = self
collectionView.backgroundColor = UIColor.cyan
return collectionView
}()
override func loadView() {
super.loadView()
self.view.addSubview(collectionView)
}
override func viewDidLoad() {
super.viewDidLoad()
setupConstraints()
}
override func viewDidAppear(_ animated: Bool) {
print("123: \(collectionView.contentSize.height)")
//prints 47 for this particular example, however doing snp.makeConstraints
//(or remakeConstraints) from here doesn't make any difference
//setupConstraints()
}
func configure(categories:[String]) {
self.categories = categories
for category in categories {
let button = BorderedButton()
button.setTitle(category, for: .normal)
buttons.append(button)
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return buttons.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath)
cell.addSubview(buttons[indexPath.row])
//prints 47 for this particular example, however doing snp.makeConstraints
//(or remakeConstraints) from here doesn't make any difference
print("XYZ: \(collectionView.contentSize.height)")
return cell
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize
{
return buttons[indexPath.row].intrinsicContentSize
}
private func setupConstraints() {
collectionView.snp.makeConstraints { (make) in
make.left.top.equalToSuperview()
make.width.equalTo(300)
print("ABC: \(collectionView.contentSize.height)")
//this does not work since height=0
make.height.equalTo(collectionView.contentSize.height)
//this works fine
make.height.equalTo(47)
}
}
}
谢谢!
编辑:添加缺失的 class
感谢您在下方回复。它确实有效,即使我错过了一半的代码,你也能得到这个问题 :-) 下面是缺少的部分,它是使用 CategoryList 的 class。在您的帮助下,我让 CategoryList 动态扩展到正确的高度,这非常好。但是,它弄乱了周围视图(下面的 view1 和 view2)的位置。 我怀疑这与
有关make.height.equalTo(categoryList.collectionView.snp.height)
行,也许它应该只引用 collectionView 约束 属性,但我不知道应该(甚至可以)怎么做?
class PlaylistDetailsHeaderView : UITableViewCell {
private var categoryList:CategoryList
private(set) lazy var view1: UIView = {
let view = UIView()
return view
}()
private(set) lazy var view2: UIView = {
let view = UIView()
return view
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
self.categoryList = CategoryList()
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(view1)
self.contentView.addSubview(view2)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure() {
let categories = ["alfa", "beta", "gamma", "fkdiii", "keoooe", "dfdsje", "jkjfldsjflsd", "alfa", "beta", "gamma", "fkdiii", "keoooe", "dfdsje", "jkjfldsjflsd"]
categoryList.configure(categories: categories)
self.contentView.addSubview(categoryList.view)
setupConstraints()
}
private func setupConstraints() {
view1.snp.makeConstraints { make in
make.left.top.equalToSuperview()
}
categoryList.view.snp.makeConstraints{ make in
make.left.equalToSuperview()
make.top.equalTo(view1.snp.bottom).offset(20)
make.width.equalTo(300)
make.height.equalTo(categoryList.collectionView.snp.height)
}
view2.snp.makeConstraints { make in
make.top.equalTo(categoryList.view.snp.bottom).offset(20)
}
}
}
如果我明白你的意思,我认为你应该这样做。
首先你需要在某处存储高度。这将默认为 0,或者在您的情况下为 47。
var heightConstraint: Constraint?
然后您将像往常一样在 snapkit 中设置约束,但是当您设置高度约束时,您将其存储在之前创建的变量中。不要忘记添加“.constraint”。
heightConstraint = make.height.equalTo(47).constraint
下一步是填充 collectionView 时,您可以再次使用 heightConstraint(如果您使变量可达)
heightConstraint.updateOffset(collectionView.contentSize.height)
Vollan 的答案非常接近。更新约束时,只需确保您在主线程上即可。所以总结一下:
var collectionViewHeightConstraint: Constraint? // on top as class property
然后在 snapkit 块内:
collectionView.snp.makeConstraints { make in
make.top.equalToSuperview() //example
collectionViewHeightConstraint = make.height.equalTo(self.collectionView.contentSize.height).constraint
}
最后但同样重要的是,当数据更新时,请不要忘记:
DispatchQueue.main.async {
self.collectionViewHeightConstraint?.update(offset: self.collectionView.contentSize.height)
}