我正在尝试与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
}}
答案 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)
}
}