CollectionView 在 StackView (Swift) 中消失
Posted
技术标签:
【中文标题】CollectionView 在 StackView (Swift) 中消失【英文标题】:CollectionView Disappears within StackView (Swift) 【发布时间】:2017-07-22 01:23:44 【问题描述】:我正在尝试实现该图中间显示的stackView排列:,但由于某种原因,包含collectionView的顶部堆栈在使用时消失了:.fill分布
stackView.distribution = .fill (stack containing collectionView disappears)
stackView.distribution = .fillEqually (collectionView appears fine in stackView)
我已经为此苦苦挣扎了好几天,你会在我注释掉的部分看到残留物:设置compressionResistance/hugging 优先级、尝试更改固有高度、更改UICollectionViewFlowLayout() 的.layout.itemSize 等。 ..在我手中没有任何作用。如果您只需将其粘贴并与空的 UIViewController 相关联,此处的代码就会运行。顶部的collectionView 堆栈包含一个pickerView,下面的堆栈是一个pageControllView、按钮的子堆栈和一个UIView。在 .fillEqually 分布中一切正常,所以这纯粹是一个布局问题。非常感谢!
// CodeStackVC2
// Test of programmatically generated stack views
// Output: nested stack views
// To make it run:
// 1) Assign the blank storyboard VC as CodeStackVC2
// 2) Move the "Is Inital VC" arrow to the blank storyboard
import UIKit
class CodeStackVC2: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate,UICollectionViewDelegateFlowLayout, UIGestureRecognizerDelegate
let fruit = ["Apple", "Orange", "Plum", "Qiwi", "Banana"]
let veg = ["Lettuce", "Carrot", "Celery", "Onion", "Brocolli"]
let meat = ["Beef", "Chicken", "Ham", "Lamb"]
let bread = ["Wheat", "Muffin", "Rye", "Pita"]
var foods = [[String]]()
let button = ["bread","fruit","meat","veg"]
var sView = UIStackView()
let cellId = "cellId"
override func viewDidLoad()
super.viewDidLoad()
foods = [fruit, veg, meat, bread]
setupViews()
//MARK: Views
lazy var cView: UICollectionView =
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 0
layout.sectionInset = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
layout.itemSize = CGSize(width: self.view.frame.width, height: 120)
let cv = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
cv.backgroundColor = UIColor.lightGray
cv.isPagingEnabled = true
cv.dataSource = self
cv.delegate = self
cv.isUserInteractionEnabled = true
// var intrinsicContentSize: CGSize
// return CGSize(width: UIViewNoIntrinsicMetric, height: 120)
//
return cv
()
lazy var pageControl: UIPageControl =
let pageC = UIPageControl()
pageC.numberOfPages = self.foods.count
pageC.pageIndicatorTintColor = UIColor.darkGray
pageC.currentPageIndicatorTintColor = UIColor.white
pageC.backgroundColor = .lightGray
pageC.addTarget(self, action: #selector(changePage(sender:)), for: UIControlEvents.valueChanged)
// pageC.setContentHuggingPriority(900, for: .vertical)
// pageC.setContentCompressionResistancePriority(100, for: .vertical)
return pageC
()
var readerView: UIView =
let rView = UIView()
rView.backgroundColor = UIColor.brown
// rView.setContentHuggingPriority(100, for: .vertical)
// rView.setContentCompressionResistancePriority(900, for: .vertical)
return rView
()
func makeButton(_ name:String) -> UIButton
let newButton = UIButton(type: .system)
let img = UIImage(named: name)?.withRenderingMode(.alwaysTemplate)
newButton.setImage(img, for: .normal)
newButton.contentMode = .scaleAspectFit
newButton.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleButton)))
newButton.isUserInteractionEnabled = true
newButton.backgroundColor = .orange
return newButton
//Make a 4-item vertical stackView containing
//cView,pageView,subStackof 4-item horiz buttons, readerView
func setupViews()
cView.register(FoodCell.self, forCellWithReuseIdentifier: cellId)
//generate an array of buttons
var buttons = [UIButton]()
for i in 0...foods.count-1
buttons += [makeButton(button[i])]
let subStackView = UIStackView(arrangedSubviews: buttons)
subStackView.axis = .horizontal
subStackView.distribution = .fillEqually
subStackView.alignment = .center
subStackView.spacing = 20
//set up the stackView
let stackView = UIStackView(arrangedSubviews: [cView,pageControl,subStackView,readerView])
stackView.axis = .vertical
//.fillEqually works, .fill deletes cView, .fillProportionally & .equalSpacing delete cView & readerView
stackView.distribution = .fillEqually
stackView.alignment = .fill
stackView.spacing = 5
//Add the stackView using AutoLayout
view.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.topAnchor.constraint(equalTo: view.topAnchor, constant: 5).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
func handleButton()
print("button pressed")
//pageControl page changer
func changePage(sender: AnyObject) -> ()
let x = CGFloat(pageControl.currentPage) * cView.frame.size.width
cView.setContentOffset(CGPoint(x:x, y:0), animated: true)
//MARK: horizontally scrolling Chapter collectionView
func scrollViewDidScroll(_ scrollView: UIScrollView)
// let scrollBarLeft = CGFloat(scrollView.contentOffset.x) / CGFloat(book.chap.count + 1)
// let scrollBarWidth = CGFloat( menuBar.frame.width) / CGFloat(book.chap.count + 1)
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>)
let index = targetContentOffset.pointee.x / view.frame.width
pageControl.currentPage = Int(index) //change PageControl indicator
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
return foods.count
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! FoodCell
cell.foodType = foods[indexPath.item]
return cell
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
return CGSize(width: view.frame.width, height: 120)
class FoodCell:UICollectionViewCell, UIPickerViewDelegate, UIPickerViewDataSource
var foodType: [String]?
didSet
pickerView.reloadComponent(0)
pickerView.selectRow(0, inComponent: 0, animated: true)
lazy var pickerView: UIPickerView =
let pView = UIPickerView()
pView.frame = CGRect(x:0,y:0,width:Int(pView.bounds.width), height:Int(pView.bounds.height))
pView.delegate = self
pView.dataSource = self
pView.backgroundColor = .darkGray
return pView
()
override init(frame: CGRect)
super.init(frame: frame)
setupViews()
func setupViews()
backgroundColor = .clear
addSubview(pickerView)
addConstraintsWithFormat("H:|[v0]|", views: pickerView)
addConstraintsWithFormat("V:|[v0]|", views: pickerView)
required init?(coder aDecoder: NSCoder)
fatalError("init(coder:) has not been implemented")
func numberOfComponents(in pickerView: UIPickerView) -> Int
return 1
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int
if let count = foodType?.count
return count
else
return 0
func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView
let pickerLabel = UILabel()
pickerLabel.font = UIFont.systemFont(ofSize: 15)
pickerLabel.textAlignment = .center
pickerLabel.adjustsFontSizeToFitWidth = true
if let foodItem = foodType?[row]
pickerLabel.text = foodItem
pickerLabel.textColor = .white
return pickerLabel
else
print("chap = nil in viewForRow")
return UIView()
【问题讨论】:
我发现collectionView(cView)在运行时的高度= 0,但在我设置的任何断点处高度= 120 - 甚至在viewDidLoad()末尾的断点!这是运行前后的控制台输出: (lldb) po cView问题是您有一个固定高度的堆栈视图,其中包含两个没有内在内容大小的视图(cView 和 readerView)。您需要告诉布局引擎它应该如何调整这些视图的大小以填充堆栈视图中的剩余空间。
当您使用 .fillEqually 分布时它会起作用,因为您告诉布局引擎使堆栈视图中的所有四个视图具有相等的高度。这定义了 cView 和 readerView 的高度。
当您使用 .fill 分布时,无法确定 cView 和 readerView 应该有多高。在您添加更多约束之前,布局是不明确的。内容优先级没有任何作用,因为这些视图没有可以拉伸或压缩的固有大小。您需要设置其中一个没有固有尺寸的视图的高度,而另一个将占用剩余空间。
问题是集合视图应该有多高?您希望它与阅读器视图的大小相同还是容器视图的某个比例?
例如,假设您的设计要求集合视图为容器视图高度的 25%,而 readerView 使用剩余空间(其他两个视图处于其自然的内在内容大小)。您可以添加以下约束:
NSLayoutConstraint.activate([
cView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.25)
])
一个更简单的例子
将布局简化为最基本的元素。您有一个堆栈视图固定到其父视图,其中有四个排列的子视图,其中两个没有固有内容大小。例如,这里有一个视图控制器,有两个普通的 UIView,一个标签和一个按钮:
class ViewController: UIViewController
override func viewDidLoad()
super.viewDidLoad()
setupViews()
private func setupViews()
let blueView = UIView()
blueView.backgroundColor = .blue
let titleLabel = UILabel()
titleLabel.text = "Hello"
let button = UIButton(type: .system)
button.setTitle("Action", for: .normal)
let redView = UIView()
redView.backgroundColor = .red
let stackView = UIStackView(arrangedSubviews: [blueView, titleLabel, button, redView])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.spacing = 8
view.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
stackView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor),
stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
blueView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.25)
])
这是它在 iPhone 上的外观,蓝色视图使用 25% 的垂直空间:
【讨论】:
【参考方案2】:UIStackView 可以很好地与 UIView 的排列子视图一起使用,但不能直接与 UICollectionView 一起使用。
我建议您将所有子视图项放在 UIView 中,然后再将它们堆叠在 UIStackView 中,您也可以使用 .fill 分布而不使用内在内容大小,而是使用约束来使您的子视图按您的需要成比例。
此解决方案还可以与自动布局无缝配合,而无需强制translatesAutoresizingMaskIntoConstraints = false,如果您明白我的意思,这会使您不太适应特征更改。
/通用汽车
【讨论】:
【参考方案3】:-
在 xib 或情节提要中设置所需控件的顶部、底部、前导和尾部约束。
提供堆栈 .fill 的分布。
然后在 Xib 或 storyboard 中提供所有堆栈的高度限制。
然后为代码中的每个堆栈设置适当的高度。
希望它对你有用。
【讨论】:
【参考方案4】:我遇到了同样的问题,对我来说,当我为放置在堆栈视图内的集合视图设置高度和宽度限制时,它就起作用了。
【讨论】:
以上是关于CollectionView 在 StackView (Swift) 中消失的主要内容,如果未能解决你的问题,请参考以下文章
CollectionView 嵌套在 Collectionview 中
无法在 collectionView 内调用 dequeueReusableCellWithReuseIdentifier(collectionView:collectionViewLayout:si
CollectionView 在 tableViewCell 数据显示
CollectionView 中的 CollectionView:使用 didSelectItemAt 执行Segue
CollectionView 里面的 CollectionView | CollectionViewCell 自动布局错误