Ios 以编程方式在UICollectionView单元格中显示UI CollectionView

Ios 以编程方式在UICollectionView单元格中显示UI CollectionView,ios,swift,uicollectionview,uicollectionviewcell,uicollectionviewflowlayout,Ios,Swift,Uicollectionview,Uicollectionviewcell,Uicollectionviewflowlayout,大家好,我正在尝试使用UICollectionView制作一个类似facebook的家庭订阅源,但在每个单元格中,我想放置另一个包含3个单元格的collectionView 我有两个错误第一个是当我在“内部集合”视图上滚动时,反弹不会使单元格回到中心。创建集合视图时,我启用了分页并将minimumLineSpacing设置为0 我不明白为什么会这样。当我试着调试时,我注意到当我删除这一行时,这个bug停止了 layout.estimatedItemSize=CGSize(宽度:cv.frame

大家好,我正在尝试使用UICollectionView制作一个类似facebook的家庭订阅源,但在每个单元格中,我想放置另一个包含3个单元格的collectionView

我有两个错误第一个是当我在“内部集合”视图上滚动时,反弹不会使单元格回到中心。创建集合视图时,我启用了分页并将minimumLineSpacing设置为0 我不明白为什么会这样。当我试着调试时,我注意到当我删除这一行时,这个bug停止了

layout.estimatedItemSize=CGSize(宽度:cv.frame.width,高度:1)

但删除这一行会给我带来这个错误

未定义UICollectionViewFlowLayout的行为,因为:项目高度必须小于UICollectionView的高度减去部分插入的顶部和底部值,减去内容插入的顶部和底部值

因为我的手机有动态高度 这里有一个例子


我的第二个问题是每个内部单元格上的文本显示好文本我必须滚动到内部集合视图的最后一个单元格才能看到这里显示的好文本就是一个例子

您的第一个问题将通过为OuterCell中的innerCollectionView设置
最小发光材料间距来解决。因此innerCollectionView的定义如下:

let innerCollectionView : UICollectionView = {
    let layout = UICollectionViewFlowLayout()
    layout.scrollDirection = .horizontal
    layout.minimumLineSpacing = 0
    layout.minimumInteritemSpacing = 0

    let cv = UICollectionView(frame :.zero , collectionViewLayout: layout)
    cv.translatesAutoresizingMaskIntoConstraints = false
    cv.backgroundColor = .orange
    layout.estimatedItemSize =  CGSize(width: cv.frame.width, height: 1)
    cv.isPagingEnabled = true
    cv.showsHorizontalScrollIndicator = false

    return cv

}()
第二个问题的解决方法是在OuterCell的post属性的didSet中添加对reloadData和layoutIfNeeded的调用,如下所示:

var post: Post? {
    didSet {
        if let numLikes = post?.numLikes {
            likesLabel.text = "\(numLikes) Likes"
        }

        if  let numComments = post?.numComments {
            commentsLabel.text = "\(numComments) Comments"
        }
        innerCollectionView.reloadData()
        self.layoutIfNeeded()
    }
}
protocol OuterCellProtocol: class {
    func changed(toPosition position: Int, cell: OutterCell)
}
public weak var delegate: OuterCellProtocol?
var positionForCell: [Int: Int] = [:]
func changed(toPosition position: Int, cell: OutterCell) {
    if let index = self.collectionView?.indexPath(for: cell) {
        self.positionForCell[index.row] = position
    }
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "OutterCardCell", for: indexPath) as! OutterCell
    cell.post = posts[indexPath.row]

    cell.delegate = self

    let cellPosition = self.positionForCell[indexPath.row] ?? 0
    cell.innerCollectionView.scrollToItem(at: IndexPath(row: cellPosition, section: 0), at: .left, animated: false)
    print (cellPosition)

    return cell
}
您所看到的与单元重用相关。如果滚动到第一个项目上的黄色边框文本,然后向下滚动,则可以看到此效果。您将看到其他人也在黄色边框的文本上(尽管现在至少有正确的文本)

编辑

作为奖励,这里有一种方法可以记住细胞的状态

首先,您需要跟踪位置何时更改,因此在OuterCell.swft中添加如下新协议:

var post: Post? {
    didSet {
        if let numLikes = post?.numLikes {
            likesLabel.text = "\(numLikes) Likes"
        }

        if  let numComments = post?.numComments {
            commentsLabel.text = "\(numComments) Comments"
        }
        innerCollectionView.reloadData()
        self.layoutIfNeeded()
    }
}
protocol OuterCellProtocol: class {
    func changed(toPosition position: Int, cell: OutterCell)
}
public weak var delegate: OuterCellProtocol?
var positionForCell: [Int: Int] = [:]
func changed(toPosition position: Int, cell: OutterCell) {
    if let index = self.collectionView?.indexPath(for: cell) {
        self.positionForCell[index.row] = position
    }
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "OutterCardCell", for: indexPath) as! OutterCell
    cell.post = posts[indexPath.row]

    cell.delegate = self

    let cellPosition = self.positionForCell[indexPath.row] ?? 0
    cell.innerCollectionView.scrollToItem(at: IndexPath(row: cellPosition, section: 0), at: .left, animated: false)
    print (cellPosition)

    return cell
}
然后将该协议的委托的实例变量添加到OuterCell类,如下所示:

var post: Post? {
    didSet {
        if let numLikes = post?.numLikes {
            likesLabel.text = "\(numLikes) Likes"
        }

        if  let numComments = post?.numComments {
            commentsLabel.text = "\(numComments) Comments"
        }
        innerCollectionView.reloadData()
        self.layoutIfNeeded()
    }
}
protocol OuterCellProtocol: class {
    func changed(toPosition position: Int, cell: OutterCell)
}
public weak var delegate: OuterCellProtocol?
var positionForCell: [Int: Int] = [:]
func changed(toPosition position: Int, cell: OutterCell) {
    if let index = self.collectionView?.indexPath(for: cell) {
        self.positionForCell[index.row] = position
    }
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "OutterCardCell", for: indexPath) as! OutterCell
    cell.post = posts[indexPath.row]

    cell.delegate = self

    let cellPosition = self.positionForCell[indexPath.row] ?? 0
    cell.innerCollectionView.scrollToItem(at: IndexPath(row: cellPosition, section: 0), at: .left, animated: false)
    print (cellPosition)

    return cell
}
最后,您需要添加以下方法,该方法在滚动结束时调用,计算新位置并调用委托方法让其知道。像这样:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    if let index = self.innerCollectionView.indexPathForItem(at: CGPoint(x: self.innerCollectionView.contentOffset.x + 1, y: self.innerCollectionView.contentOffset.y + 1)) {
        self.delegate?.changed(toPosition: index.row, cell: self)
    }
}
这就是每个单元格检测集合视图单元格何时更改并通知委托。让我们看看如何使用这些信息

OutterCellCollectionViewController需要跟踪每个单元格在其集合视图中的位置,并在它们可见时进行更新

因此,首先要使OutterCellCollectionViewController符合OuterCellProtocol,以便在其

class OutterCellCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, OuterCellProtocol   {
然后添加一个类实例变量,将单元格位置记录到OuterCellCollectionViewController,如下所示:

var post: Post? {
    didSet {
        if let numLikes = post?.numLikes {
            likesLabel.text = "\(numLikes) Likes"
        }

        if  let numComments = post?.numComments {
            commentsLabel.text = "\(numComments) Comments"
        }
        innerCollectionView.reloadData()
        self.layoutIfNeeded()
    }
}
protocol OuterCellProtocol: class {
    func changed(toPosition position: Int, cell: OutterCell)
}
public weak var delegate: OuterCellProtocol?
var positionForCell: [Int: Int] = [:]
func changed(toPosition position: Int, cell: OutterCell) {
    if let index = self.collectionView?.indexPath(for: cell) {
        self.positionForCell[index.row] = position
    }
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "OutterCardCell", for: indexPath) as! OutterCell
    cell.post = posts[indexPath.row]

    cell.delegate = self

    let cellPosition = self.positionForCell[indexPath.row] ?? 0
    cell.innerCollectionView.scrollToItem(at: IndexPath(row: cellPosition, section: 0), at: .left, animated: false)
    print (cellPosition)

    return cell
}
然后添加所需的OuterCellProtocol方法来记录单元格位置的变化,如下所示:

var post: Post? {
    didSet {
        if let numLikes = post?.numLikes {
            likesLabel.text = "\(numLikes) Likes"
        }

        if  let numComments = post?.numComments {
            commentsLabel.text = "\(numComments) Comments"
        }
        innerCollectionView.reloadData()
        self.layoutIfNeeded()
    }
}
protocol OuterCellProtocol: class {
    func changed(toPosition position: Int, cell: OutterCell)
}
public weak var delegate: OuterCellProtocol?
var positionForCell: [Int: Int] = [:]
func changed(toPosition position: Int, cell: OutterCell) {
    if let index = self.collectionView?.indexPath(for: cell) {
        self.positionForCell[index.row] = position
    }
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "OutterCardCell", for: indexPath) as! OutterCell
    cell.post = posts[indexPath.row]

    cell.delegate = self

    let cellPosition = self.positionForCell[indexPath.row] ?? 0
    cell.innerCollectionView.scrollToItem(at: IndexPath(row: cellPosition, section: 0), at: .left, animated: false)
    print (cellPosition)

    return cell
}
最后更新cellForItemAt方法以设置单元格的委托,并使用新的单元格位置,如下所示:

var post: Post? {
    didSet {
        if let numLikes = post?.numLikes {
            likesLabel.text = "\(numLikes) Likes"
        }

        if  let numComments = post?.numComments {
            commentsLabel.text = "\(numComments) Comments"
        }
        innerCollectionView.reloadData()
        self.layoutIfNeeded()
    }
}
protocol OuterCellProtocol: class {
    func changed(toPosition position: Int, cell: OutterCell)
}
public weak var delegate: OuterCellProtocol?
var positionForCell: [Int: Int] = [:]
func changed(toPosition position: Int, cell: OutterCell) {
    if let index = self.collectionView?.indexPath(for: cell) {
        self.positionForCell[index.row] = position
    }
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "OutterCardCell", for: indexPath) as! OutterCell
    cell.post = posts[indexPath.row]

    cell.delegate = self

    let cellPosition = self.positionForCell[indexPath.row] ?? 0
    cell.innerCollectionView.scrollToItem(at: IndexPath(row: cellPosition, section: 0), at: .left, animated: false)
    print (cellPosition)

    return cell
}

如果您成功地获得了正确的所有设置,当您上下滚动列表时,它应该跟踪位置。

那么第一个问题已经解决了吗?第二个问题是什么?没有任何一个问题在第一个Gif上是固定的您可以看到最后单元格没有返回到中心我的第二个问题是每个内部单元格上的文本显示好文本我必须滚动到内部集合视图的最后一个单元格才能看到显示的好文本这里是一个示例您可以看到第二,你说的“好”文本是什么意思?我指的是假定存在的文本。我有一个post数组,每个post都有一个字符串数组,因此要创建所有外部单元格,我使用Posts.count。然后我必须创建内部单元格,这样我就要在cellForItemAt indexPath函数中使用Posts.listText.count,我要做cell.text=listText[indexPath.row]但是indexPath.row不是好的,你可以在第二张gif上看到,我在Lorem Ipsum之前在单元格上打印的索引路径不是好的,但是在我滚动并返回后,会显示好的。非常感谢你的帮助,但是我尝试了你的代码,我有一些问题,你的解决方案真的有效,但我想知道如果现在我正在更新布局,这一行真的很有用,那么它似乎没有什么用处。estimatedItemSize=CGSize(宽度:cv.frame.width,高度:1)另一个问题是,当我们在外部单元格上滚动一个单元格时,其他随机单元格为什么会被滚动到…虽然严格来说没有必要,但给EstimatedItemize提供某种合理的默认值总是好的做法,而且这样做并没有坏处。文档中说这样做有助于提高性能。您认为创建innerCell的2个子类并注册它们以防止重用更好吗?滚动问题创建额外的子类不会阻止单元重用问题。你想让单元格在上下滚动列表时记住它们是如何滚动集合视图的,还是它们都可以返回到第一个文本项?我想让它们记住它们在哪里