CollectionView 在 StackView (Swift) 中消失 [英] CollectionView Disappears within StackView (Swift)

查看:28
本文介绍了CollectionView 在 StackView (Swift) 中消失的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现此图中间所示的stackView排列:,但由于某种原因,包含 collectionView 的顶部堆栈在使用时消失:.fill 分布

I'm trying to achieve the stackView arrangement shown in the middle of this figure:, but for some reason the top stack, containing a collectionView, disappears when using: a .fill distribution

stackView.distribution = .fill        (stack containing collectionView disappears)
stackView.distribution = .fillEqually (collectionView appears fine in stackView)

我已经为此苦苦挣扎了好几天,您会在我注释掉的部分中看到残留物:设置压缩阻力/拥抱优先级、尝试更改固有高度、更改 UICollectionViewFlowLayout() 的 .layout.itemSize 等...在我手中没有任何作用.如果您只是将其粘贴并与一个空的 UIViewController 相关联,这里的代码就会运行.顶部的 collectionView 堆栈包含一个 pickerView,下面的堆栈是 pageControllView、按钮的 subStack 和一个 UIView.在 .fillEqually 分布中一切正常,所以这纯粹是一个布局问题.非常感谢!

I've been struggling with this for days, and you'll see residues in my commented out sections: setting compressionResistance/hugging priorities, attempting to change the intrinsic height, changing .layout.itemSize of UICollectionViewFlowLayout(), etc... Nothing works in my hands. The code here will run if you simply paste it in and associate it with an empty UIViewController. The top, collectionView stack contains a pickerView, and the stacks below that are a pageControllView, subStack of buttons, and a UIView. It all works fine in the .fillEqually distribution, so this is purely a layout issue. Much Thanks!

// 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()
    }
  }


}

推荐答案

问题是您有一个具有固定高度的堆栈视图,其中包含两个没有固有内容大小的视图(cView 和 readerView).您需要告诉布局引擎它应该如何调整这些视图的大小以填充堆栈视图中的剩余空间.

The problem is that you have a stack view with a fixed height that contains two views (cView and readerView) that have no intrinsic content size. You need to tell the layout engine how it should size those views to fill the remaining space in the stack view.

当您使用 .fillEqually 分布时它会起作用,因为您告诉布局引擎使堆栈视图中的所有四个视图具有相同的高度.这定义了 cView 和 readerView 的高度.

It works when you use a .fillEqually distribution because you are telling the layout engine to make all four views in the stack view have an equal height. That defines a height for both the cView and readerView.

当您使用 .fill 分布时,无法确定 cView 和 readerView 应该有多高.在您添加更多约束之前,布局是不明确的.内容优先级没有任何作用,因为这些视图没有可以拉伸或压缩的内在尺寸.您需要设置一个没有固有大小的视图的高度,另一个将占用剩余空间.

When you use a .fill distribution there is no way to determine how high the cView and readerView should be. The layout is ambiguous until you add more constraints. The content priorities do nothing as those views have no intrinsic size that can be stretched or squeezed. You need to set the height of one of the views with no intrinsic size and the other will take the remaining space.

问题是集合视图应该多高?您希望它与阅读器视图的大小相同还是容器视图的某个比例?

The question is how high should the collection view be? Do you want it to be the same size as the reader view or maybe some proportion of the container view?

例如,假设您的设计要求集合视图为容器视图高度的 25%,而 readerView 使用剩余空间(另外两个视图处于其自然的内在内容大小).您可以添加以下约束:

For example, suppose your design calls for the collection view to be 25% of the height of the container view with the readerView using the remaining space (the two other views are at their natural intrinsic content size). You could add the following constraint:

NSLayoutConstraint.activate([
  cView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.25)
])

一个更简单的例子

将布局减少到最基本的元素.您有一个堆栈视图固定到其超视图,其中有四个排列的子视图,其中两个没有固有内容大小.例如,这是一个带有两个普通 UIView、一个标签和一个按钮的视图控制器:

A Simpler Example

To reduce the layout to its most basic elements. You have a stack view pinned to its superview with four arranged subviews two of which have no intrinsic content size. For example, here is a view controller with two plain UIView, a label and a button:

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)
      ])
  }
}

使用 25% 的垂直空间,在 iPhone 纵向显示蓝色视图的效果如下:

Here is how it looks on an iPhone in portrait with the blue view using 25% of the vertical space:

这篇关于CollectionView 在 StackView (Swift) 中消失的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆