在可滚动列表中快速添加项目

时间:2019-10-21 16:00:24

标签: ios swift listview uiscrollview

现在这就是我项目中的全部内容:

enter image description here

最后,它的外观和功能应如下所示:

enter image description here enter image description here enter image description here

1。。如何将项目添加到ScrollView(以2 x X视图显示)

2。。如何使ScrollView真正能够滚动(并刷新,如下面的3张图片所示),或者仅用一个列表就可以解决?

更新

最终视图应如下所示:

enter image description here

“ MainWishList”单元格和“ neue Liste erstellen”(=添加新单元格)应该从头开始。当用户单击“添加单元格”时,他应该能够为列表选择名称和图像。

1 个答案:

答案 0 :(得分:1)

UICollectionView的内置功能的一部分是,当您有更多的项目(单元格)超出框架容纳的范围时,将自动滚动。因此,无需在滚动视图中嵌入集合视图。

这是一个基本示例。一切都通过代码完成(没有@IBOutlet@IBAction或原型单元)。创建一个新的UIViewController并将其类分配给ExampleViewController,如下所示:

//
//  ExampleViewController.swift
//  CollectionAddItem
//
//  Created by Don Mag on 10/22/19.
//

import UIKit

// simple cell with label
class ContentCell: UICollectionViewCell {

    let theLabel: UILabel = {
        let v = UILabel()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.textAlignment = .center
        return v
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }

    func commonInit() -> Void {
        contentView.backgroundColor = .yellow
        contentView.addSubview(theLabel)
        // constrain label to all 4 sides
        NSLayoutConstraint.activate([
            theLabel.topAnchor.constraint(equalTo: contentView.topAnchor),
            theLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
            theLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
            theLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
        ])
    }

}

// simple cell with button
class AddItemCell: UICollectionViewCell {

    let btn: UIButton = {
        let v = UIButton()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.setTitle("+", for: .normal)
        v.setTitleColor(.systemBlue, for: .normal)
        v.titleLabel?.font = UIFont.systemFont(ofSize: 40.0)
        return v
    }()

    // this will be used as a "callback closure" in collection view controller
    var tapCallback: (() -> ())?

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }

    func commonInit() -> Void {
        contentView.backgroundColor = .green
        contentView.addSubview(btn)
        // constrain button to all 4 sides
        NSLayoutConstraint.activate([
            btn.topAnchor.constraint(equalTo: contentView.topAnchor),
            btn.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
            btn.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
            btn.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
        ])
        btn.addTarget(self, action: #selector(didTap(_:)), for: .touchUpInside)
    }

    @objc func didTap(_ sender: Any) {
        // tell the collection view controller we got a button tap
        tapCallback?()
    }

}

class ExampleViewController: UIViewController, UICollectionViewDataSource {

    let theCollectionView: UICollectionView = {
        let v = UICollectionView(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout())
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .white
        v.contentInsetAdjustmentBehavior = .always
        return v
    }()

    let columnLayout = FlowLayout(
        itemSize: CGSize(width: 100, height: 100),
        minimumInteritemSpacing: 10,
        minimumLineSpacing: 10,
        sectionInset: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
    )

    // track collection view frame change
    var colViewWidth: CGFloat = 0.0

    // example data --- this will be filled with simple number strings
    var theData: [String] = [String]()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .systemYellow

        view.addSubview(theCollectionView)

        // constrain collection view
        //      100-pts from top
        //      60-pts from bottom
        //      40-pts from leading
        //      40-pts from trailing
        NSLayoutConstraint.activate([
            theCollectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 100.0),
            theCollectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -60.0),
            theCollectionView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 40.0),
            theCollectionView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -40.0),
        ])

        // register the two cell classes for reuse
        theCollectionView.register(ContentCell.self, forCellWithReuseIdentifier: "ContentCell")
        theCollectionView.register(AddItemCell.self, forCellWithReuseIdentifier: "AddItemCell")

        // set collection view dataSource
        theCollectionView.dataSource = self

        // use custom flow layout
        theCollectionView.collectionViewLayout = columnLayout

    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        // only want to call this when collection view frame changes
        // to set the item size
        if theCollectionView.frame.width != colViewWidth {
            let w = theCollectionView.frame.width / 2 - 15
            columnLayout.itemSize = CGSize(width: w, height: w)
            colViewWidth = theCollectionView.frame.width
        }
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        // return 1 more than our data array (the extra one will be the "add item" cell
        return theData.count + 1
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        // if item is less that data count, return a "Content" cell
        if indexPath.item < theData.count {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ContentCell", for: indexPath) as! ContentCell
            cell.theLabel.text = theData[indexPath.item]
            return cell
        }

        // past the end of the data count, so return an "Add Item" cell
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AddItemCell", for: indexPath) as! AddItemCell

        // set the closure
        cell.tapCallback = {
            // add item button was tapped, so append an item to the data array
            self.theData.append("\(self.theData.count + 1)")
            // reload the collection view
            collectionView.reloadData()
            collectionView.performBatchUpdates(nil, completion: {
                (result) in
                // scroll to make newly added row visible (if needed)
                let i = collectionView.numberOfItems(inSection: 0) - 1
                let idx = IndexPath(item: i, section: 0)
                collectionView.scrollToItem(at: idx, at: .bottom, animated: true)
            })
        }

        return cell

    }

}


// custom FlowLayout class to left-align collection view cells
// found here: https://stackoverflow.com/a/49717759/6257435
class FlowLayout: UICollectionViewFlowLayout {

    required init(itemSize: CGSize, minimumInteritemSpacing: CGFloat = 0, minimumLineSpacing: CGFloat = 0, sectionInset: UIEdgeInsets = .zero) {
        super.init()

        self.itemSize = itemSize
        self.minimumInteritemSpacing = minimumInteritemSpacing
        self.minimumLineSpacing = minimumLineSpacing
        self.sectionInset = sectionInset
        sectionInsetReference = .fromSafeArea
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let layoutAttributes = super.layoutAttributesForElements(in: rect)!.map { $0.copy() as! UICollectionViewLayoutAttributes }
        guard scrollDirection == .vertical else { return layoutAttributes }

        // Filter attributes to compute only cell attributes
        let cellAttributes = layoutAttributes.filter({ $0.representedElementCategory == .cell })

        // Group cell attributes by row (cells with same vertical center) and loop on those groups
        for (_, attributes) in Dictionary(grouping: cellAttributes, by: { ($0.center.y / 10).rounded(.up) * 10 }) {
            // Set the initial left inset
            var leftInset = sectionInset.left

            // Loop on cells to adjust each cell's origin and prepare leftInset for the next cell
            for attribute in attributes {
                attribute.frame.origin.x = leftInset
                leftInset = attribute.frame.maxX + minimumInteritemSpacing
            }
        }

        return layoutAttributes
    }
}

运行此命令时,数据数组将为空,因此您首先会看到的是:

enter image description here

每次单击“ +”单元格,都会将新项添加到数据数组(在本示例中为数字字符串),将调用reloadData(),并出现一个新单元格。 / p>

enter image description here

一旦我们的数据数组中有足够的项目,因此它们无法全部放入集合视图框架中,则集合视图将变为可滚动的:

enter image description here