【问题标题】:Max width for content view of UICollectionViewUICollectionView 内容视图的最大宽度
【发布时间】:2022-01-14 10:09:14
【问题描述】:

UI 中的一个常见模式是将视图的大小最大化到某个点,然后用空格填充其父视图的其余部分。

使用 AutoLayout 时,可以通过 width

因此,我发现实现该行为的唯一方法是在单元格内部使用正确的布局。我认为这是一个不太好的设计决策(尤其是当您有多个单元格时)。但是有什么替代品吗?

【问题讨论】:

  • 真的不清楚你在问什么。您是在谈论水平滚动集合视图吗?还是垂直的? “侧面不可滚动” 是什么意思?也许显示您要完成的任务的屏幕截图?
  • 就我而言,这是一个水平集合视图。如果你用 width
  • 好的 - 只是为了清楚一点......你有一个水平集合视图,它小于其父视图的宽度,并且你希望能够通过触摸并从区域拖动到左和右?就像在这张图片中一样(红色虚线轮廓显示您希望能够拖动的位置)? i.stack.imgur.com/er870.png

标签: ios uicollectionview uicollectionviewlayout


【解决方案1】:

我们可以通过继承UICollectionView 并实现hitTest(_:with:) 来实现这一点。

我们要做的是将“触摸区域”扩展得比集合视图本身更宽:

class ExtendedCollectionView: UICollectionView {
    
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {

        if self.bounds.contains(point) {
            // touch is inside self.bounds, so
            //  send it on to super (i.e. "normal" behavior)
            //  so we can select a cell on tap
            return super.hitTest(point, with: event)
        }

        if self.bounds.insetBy(dx: -self.frame.origin.x, dy: 0).contains(point) {
            // touch is outside self.bounds, but
            //  it IS inside bounds extended left and right, so
            //  capture the touch for self
            return self
        }

        // touch was outside self.bounds (and outside our extended bounds), so
        //  send it on to super (i.e. "normal" behavior)
        //  so the rest of the view hierarchy (buttons, etc)
        //      can receive the gesture
        return super.hitTest(point, with: event)
        
    }
    
}

您现在可以像使用 UICollectionView 一样使用 ExtendedCollectionView,但您可以从左侧或右侧开始滚动它,超出其范围。

这是一个完整的例子:

class ViewController: UIViewController {
    
    var myData: [String] = []
    
    // we'll use these colors for the cell backgrounds
    let colors: [UIColor] = [
        .systemRed, .systemGreen, .systemBlue,
        .systemPink, .systemYellow, .systemTeal,
    ]
    
    // our "extended collection view"
    var collectionView: ExtendedCollectionView!
    
    let cellSize: CGSize = CGSize(width: 80, height: 100)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.title = "Extended CollectionView"
        
        // always respect the safe area
        let g = view.safeAreaLayoutGuide
        
        // let's add a "background" image view, sized to fit the view
        //  so we can easily see the reults
        if let img = UIImage(named: "sampleBKG") {
            let v = UIImageView()
            v.image = img
            v.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(v)
            view.sendSubviewToBack(v)
            NSLayoutConstraint.activate([
                v.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
                v.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
                v.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
                v.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
            ])
        }
        
        // fill myData array with 20 strings
        //  of different lengths to show this
        //  works with dynamic width cells
        let strs: [String] = [
            "Short",
            "Bit Longer",
            "Much Longer String",
        ]
        for i in 0..<20 {
            myData.append("C: \(i) \(strs[i % strs.count])")
        }
        
        
        // set the flow layout properties
        let fl = UICollectionViewFlowLayout()
            fl.estimatedItemSize = CGSize(width: 50, height: 100)
        fl.scrollDirection = .horizontal
            fl.minimumLineSpacing = 8
            fl.minimumInteritemSpacing = 8

        // create an instance of ExtendedCollectionView
        collectionView = ExtendedCollectionView(frame: .zero, collectionViewLayout: fl)
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.backgroundColor = .clear
        view.addSubview(collectionView)
        
        // let's make the collection view
        //  80% of the width of the view's safe area
        let cvWidthPercent = 0.8
        
        // let's add a label below our custom view
        //  the same percentage width, so we can
        //  easily see the layout
        let v = UILabel()
        v.backgroundColor = .green
        v.translatesAutoresizingMaskIntoConstraints = false
        v.textAlignment = .center
        v.text = "\(cvWidthPercent * 100)%"
        view.addSubview(v)
        
        NSLayoutConstraint.activate([
            
            // let's put our collection view
            // 80-pts from the top
            collectionView.topAnchor.constraint(equalTo: g.topAnchor, constant: 80.0),
            // centered Horizontally
            collectionView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            //  height equal to cell Height
            collectionView.heightAnchor.constraint(equalToConstant: cellSize.height),
            // 80% of the width of the safe area
            collectionView.widthAnchor.constraint(equalTo: g.widthAnchor, multiplier: cvWidthPercent),

            // constrain label 8-pts below the collection view
            v.topAnchor.constraint(equalTo: collectionView.bottomAnchor, constant: 8.0),
            // centered Horizontally
            v.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            // same percentage width
            v.widthAnchor.constraint(equalTo: g.widthAnchor, multiplier: cvWidthPercent),
            
        ])
        
        collectionView.dataSource = self
        collectionView.delegate = self
        
        collectionView.register(MyDynamicCVCell.self, forCellWithReuseIdentifier: "cvCell")
        
    }
    
}

extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return myData.count
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cvCell", for: indexPath) as! MyDynamicCVCell
        cell.contentView.backgroundColor = colors[indexPath.item % colors.count]
        cell.label.text = myData[indexPath.item]
        return cell
    }
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        print("Did Select Cell At:", indexPath)
    }
}

一个简单的动态宽度单元格

class MyDynamicCVCell: UICollectionViewCell {
    let label: UILabel = {
        let v = UILabel()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.textAlignment = .center
        v.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
        return v
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        contentView.addSubview(label)
        NSLayoutConstraint.activate([
            label.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor),
            label.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor),
            label.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
        ])
    }
}

结果如下:

当你运行它时,你会看到你可以水平滚动,即使你从单元格的左边或右边开始拖动。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多