代码之家  ›  专栏  ›  技术社区  ›  Tony M

CollectionView在StackView中消失(Swift)

  •  2
  • Tony M  · 技术社区  · 8 年前

    the middle of this figure ,但由于某些原因,当使用:a.fill分布时,包含collectionView的顶部堆栈消失

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

    我已经为此挣扎了好几天,你会在我评论过的部分中看到残留物:设置压缩阻力/拥抱优先级,尝试改变固有高度,改变布局。UICollectionViewFlowLayout()的itemSize等…我手里什么都用不了。如果您只需将其粘贴到中并将其与空的UIViewController关联,则此处的代码将运行。顶部的collectionView堆栈包含一个pickerView,下面的堆栈是PageControlView、按钮子堆栈和UIView。它在.fillEqualize分布中运行良好,因此这纯粹是一个布局问题。非常感谢!

    // 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()
        }
      }
    
    
    }
    
    5 回复  |  直到 8 年前
        1
  •  14
  •   kharrison    8 年前

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

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

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

    例如,假设您的设计要求集合视图为容器视图高度的25%,readerView使用剩余空间(其他两个视图为其固有内容大小)。可以添加以下约束:

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

    将布局简化为最基本的元素。将堆栈视图固定到其superview,其中有四个排列的子视图,其中两个没有固有的内容大小。例如,这是一个视图控制器,具有两个普通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%的垂直空间:

    iPhone Screenshot

        2
  •  1
  •   Giuseppe Mazzilli    6 年前

    UIStackView可以很好地处理作为UIView的排列子视图,但不能直接与UICollectionView配合使用。

    我建议您在将所有子视图项堆叠到UIStackView中之前,将它们放置在UIView中,您也可以使用。填充分布而不使用内部内容大小,使用约束使子视图与您需要的成比例。

    此解决方案还可以无缝地与自动布局配合使用,无需强制 translatesAutoresizingMaskIntoConstraints=false 如果你知道我的意思的话,这会让你对特质变化不那么顺从。

    /通用汽车公司

        3
  •  0
  •   Suraj Rao Raas Masood    6 年前
    1. 提供烟囱的分布。填充。

    希望它对你有用。

        4
  •  0
  •   Naval Hasan    5 年前

    我也有同样的问题,对我来说,当我给放置在堆栈视图中的集合视图提供高度和宽度约束时,它起到了作用。

        5
  •  0
  •   Victor Thomas Wilcox Jr.    3 年前

    我在Xamarin CollectionView中体验到了这种行为,并跟踪到在web api调用将页面从主页删除后与CollectionView进行的交互。即使阻止了这一点,但重新加载页面仍然存在问题。最后,我决定在页面即将隐藏时清除集合列表,并保存项目的备份副本,然后在页面显示时,运行一个等待10毫秒的异步任务,然后重新安装项目。未能清除列表或在重新显示后立即将项目安装到列表中都会导致错误。控制台列表中显示了以下内容,CollectionView似乎标记自己,以便在此消息之后尝试更长时间工作:

    2022-04-16 19:56:33.760310-0500。iOS[30135:2117558]未定义UICollectionViewFlowLayout的行为,因为: 2022-04-16 19:56:33.760454-0500。iOS[30135:2117558]项目宽度必须小于UICollectionView的宽度减去部分插入的左侧和右侧值,减去内容插入的左侧和右侧值。 2022-04-16 19:56:33.760754-0500。iOS[30135:2117558]相关的UICollectionViewFlowLayout实例是<Xamarin\u Forms\u Platform\u iOS\u ListViewLayout:0x7f99e4c4e890>,并附于<UICollectionView:0x7f99e562a000;帧=(0;420 695);clipstobunds=是;自动调整大小=W+H;手势识别器=<N阵列:0x6000015ad9b0>;层=<CALayer:0x600005be5860>;contentOffset:{0,0};内容大小:{0,0};adjustedContentInset:{0,0,0,0};布局:<Xamarin_Forms_Platform_iOS_ListViewLayout:0x7f99e4c4e890>;数据源:<Xamarin\u Forms\u Platform\u iOS\u GroupableItemsViewController\u 1:0x7f99e4c7ace0>>。 2022-04-16 19:56:33.760829-0500。iOS[30135:2117558]在UICollectionViewFlowLayoutBreakForInvalidSizes处创建一个符号断点,以在调试器中捕捉到该断点。