自定义UICollectionViewFlowLayout无法正常工作

时间:2017-12-11 01:39:34

标签: ios swift

我正在尝试与Apple News应用程序类似的自定义Flow Layout flowLayout.delegate = self方法采用viewDidLoad(),而我的网络代码位于async的{​​{1}}方法中。 问题是在我可以从服务器检索所有数据之前调用自定义流布局的方法,因此应用程序崩溃。 关于如何使其发挥作用的任何想法?这是我的viewDidAppear()实施:

ViewController

}

  

下面是新闻的结构:

class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, AppleNewsFlowLayoutDelegate {

    @IBOutlet weak var collectionView: UICollectionView!
    @IBOutlet weak var flowLayout: AppleNewsFlowLayout!

    var newsArray = [News]()
    var getFromDb = GetFromDb()

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        if getFromDb.news.isEmpty {
            loadStore()
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.dataSource = self
        collectionView.delegate = self
        flowLayout.delegate = self
    }

    func loadStore() {
        let urlString = "https://url"
        self.getFromDb.getBreaksFromDb(url: urlString) { (breaksDataCell) in
            if !breaksDataCell.isEmpty {
                DispatchQueue.main.async(execute: {
                    self.collectionView.reloadData()
                })
            }
        }
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return getFromDb.news.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        // Filling the cells with the correct info...
        return cell
    }

func AppleNewsFlowLayout(_ AppleNewsFlowLayout: AppleNewsFlowLayout, cellTypeForItemAt indexPath: IndexPath) -> NewsCellType {
    return getFromDb.news[indexPath.row].cellType
}
  

Flow Layout类

struct News {
let image: String
let provider: String
let title: String
let cellType: NewsCellType

init(image: String, provider: String, title: String, cellType: NewsCellType) {
    self.image = image
    self.provider = provider
    self.title = title
    self.cellType = cellType
}}

2 个答案:

答案 0 :(得分:0)

处理自定义集合视图流布局时需要做的两件事。

自定义流程布局中的prepare()方法更改。只有当项目数超过0时,您才会开始准备布局。

private var numberOfItems:Int{
    return (collectionView?.numberOfItems(inSection: 0))!
}

override func prepare() {
        for item in 0 ..< numberOfItems{ }
}

使用 numberOfItems 属性,只要您想在customFlowLayout中对collectionView项执行某些操作,以避免崩溃。

答案 1 :(得分:0)

如果您希望实现类似于带网络的Apple News应用程序的自定义集合视图流布局,请在下面显示正常运行的干净代码:

class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, AppleNewsFlowLayoutDelegate {


@IBOutlet weak var collectionView: UICollectionView!
@IBOutlet weak var flowLayout: AppleNewsFlowLayout!
var getFromDb = GetFromDb()

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    if getFromDb.news.isEmpty {
        loadStore()
    }
}


override func viewDidLoad() {
    super.viewDidLoad()
    collectionView.dataSource = self
    collectionView.delegate = self
    flowLayout.delegate = self
}


func loadStore() {
    let urlString = "https://yourURL"
    self.getFromDb.getBreaksFromDb(url: urlString) { (breaksDataCell) in
        if !breaksDataCell.isEmpty {
            DispatchQueue.main.async(execute: {
                self.collectionView.reloadData()
                print("RELOAD")
            })
        }
    }
}


func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return getFromDb.news.count
}


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

    let news = getFromDb.news[indexPath.row]
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: news.cellType.rawValue, for: indexPath) as! NewCell
    cell.image.image = UIImage(named: "news")!
    cell.provider.text = news.provider
    cell.title.text = news.title

    return cell
}


func AppleNewsFlowLayout(_ AppleNewsFlowLayout: AppleNewsFlowLayout, cellTypeForItemAt indexPath: IndexPath) -> NewsCellType {
    print(getFromDb.news[indexPath.row].cellType)
    return getFromDb.news[indexPath.row].cellType
}
}

这是Flow Layout类:

protocol AppleNewsFlowLayoutDelegate: class {
func AppleNewsFlowLayout(_ AppleNewsFlowLayout: AppleNewsFlowLayout, cellTypeForItemAt indexPath: IndexPath) -> NewsCellType
}

class AppleNewsFlowLayout: UICollectionViewFlowLayout {
var maxY: CGFloat = 0.0
var isVSetOnce = false
weak var delegate: AppleNewsFlowLayoutDelegate?
var attributesArray: [UICollectionViewLayoutAttributes]?
private var cache = [UICollectionViewLayoutAttributes]()

private var numberOfItems:Int{
    return (collectionView?.numberOfItems(inSection: 0))!
}

override func prepare() {
    super.prepare()

        minimumLineSpacing = 10
        minimumInteritemSpacing = 16
        sectionInset = UIEdgeInsets(top: 10, left: 16, bottom: 10, right: 16)

        guard cache.isEmpty == true, let collectionView = collectionView else {
            return
        }

        let screenWidth = UIScreen.main.bounds.width
        var x = sectionInset.left
        maxY = maxY + minimumLineSpacing

        for item in 0 ..< collectionView.numberOfItems(inSection: 0) {
            let indexPath = IndexPath(item: item, section: 0)
            guard let newsType: NewsCellType = delegate?.AppleNewsFlowLayout(self, cellTypeForItemAt: indexPath) else {
                fatalError("AppleNewsFlowLayoutDelegate method is required.")
            }
            let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)

            switch newsType {

            case .big:
                let width = screenWidth - sectionInset.left - sectionInset.right
                attributes.frame = CGRect(x: x, y: maxY, width: width, height: width * 1.2)
                maxY += width * 1.2

            case .horizontal:
                let width = screenWidth - sectionInset.left - sectionInset.right
                attributes.frame = CGRect(x: x, y: maxY, width: width, height: 150)
                maxY += 150

            case .vertical:
                let width = (screenWidth - minimumInteritemSpacing - sectionInset.left - sectionInset.right) / 2
                x = isVSetOnce ? x + width + minimumInteritemSpacing : x
                maxY = isVSetOnce ? maxY-10 : maxY
                attributes.frame = CGRect(x: x, y: maxY, width: width, height: screenWidth * 0.8)
                if isVSetOnce {
                    maxY += screenWidth * 0.8
                }
                isVSetOnce = !isVSetOnce
            }
            cache.append(attributes)
        }
}

override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    var layoutAttributes = [UICollectionViewLayoutAttributes]()
    for attribute in cache{
        if attribute.frame.intersects(rect){
            layoutAttributes.append(attribute)
        }
    }
    return layoutAttributes
}


override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    return cache[indexPath.item]
}


override var collectionViewContentSize: CGSize {
    return CGSize(width: UIScreen.main.bounds.width, height: maxY)
}
}