1

我正在尝试使用 实现无限滚动IGListKit。我遵循了他们的示例代码,但是这是一个简单的文本单元格。

我正在使用渲染图像的自定尺寸单元格。

我的图像使用Kingfisher. 我希望滚动视图保持其位置并在下面附加任何新项目,从本质上增加可滚动区域。

相反,我的提要在添加新项目后跳回第一个项目。

我在我的方法中模拟了网络调用的延迟scrollViewWillEndDragging,将新项目附加到集合的末尾并在触发集合adapter.performUpdates(animated: false)didSet调用。

我正在渲染的模型是

class FeedImage {
    let id = UUID()
    let url: URL
    let width: CGFloat
    let height: CGFloat

    init(url: String, size: CGSize) {
        self.url = URL(string: url)!
        self.width = size.width
        self.height = size.height
    }

    var heightForFeed: CGFloat {
        let ratio = CGFloat(width) / CGFloat(height)
        let height = (UIScreen.main.bounds.width - 32) / ratio
        return height
    }
}

extension FeedImage: ListDiffable {
    func isEqual(toDiffableObject object: ListDiffable?) -> Bool {
        if let object = object as? FeedImage {
            return url == object.url
        }
        return false
    }

    func diffIdentifier() -> NSObjectProtocol {
        return id as NSObjectProtocol
    }
}

您可以在此处查看行为。

ImageFeedViewController

class ImageFeedViewController: UIViewController {

    let vOne = [
        FeedImage(url: "https://pbs.twimg.com/media/D-D70C8W4AEtCav.jpg", size: .init(width: 1280, height: 1657)),
        FeedImage(url: "https://pbs.twimg.com/media/D-DpfrbWsAI6KaC.jpg", size: .init(width: 911, height: 683)),
        FeedImage(url: "https://pbs.twimg.com/media/D-EU-79W4AAlIk6.jpg", size: .init(width: 499, height: 644 )),
        FeedImage(url: "https://pbs.twimg.com/media/D-ECq-dX4AA1U40.jpg", size: .init(width: 1035, height: 1132)),
    ]

    var feed = [FeedImage]() {
        didSet {
            self.adapter.performUpdates(animated: false)
            loading = false
        }
    }

    var loading = false
    var hasHadded = false // prevent mock pagination from firing over and over

    private lazy var collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.estimatedItemSize = .init(width: view.frame.width, height: 10)

        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.alwaysBounceVertical = true
        collectionView.backgroundColor = .darkGray

        return collectionView
    }()

    private lazy var adapter: ListAdapter = {
        return ListAdapter(
            updater: ListAdapterUpdater(),
            viewController: self,
            workingRangeSize: 0)
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        adapter.collectionView = collectionView
        adapter.dataSource = self
        adapter.scrollViewDelegate = self

        view.addSubview(collectionView)

        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(equalTo: view.topAnchor),
            collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])

        DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
            self.feed = self.vOne
        }
    }
}

extension ImageFeedViewController: ListAdapterDataSource {
    func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
        return feed as [ListDiffable]
    }

    func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController {
        return ImageSectionController()
    }

    func emptyView(for listAdapter: ListAdapter) -> UIView? {
        return nil
    }
}

extension ImageFeedViewController: UIScrollViewDelegate {
    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        let distance = scrollView.contentSize.height - (targetContentOffset.pointee.y + scrollView.bounds.height)

        if distance < 200 && !loading && !hasHadded {
            loading = true
            hasHadded = true
            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                self.feed.append(contentsOf: [
                    FeedImage(url: "https://pbs.twimg.com/media/D-EL1DNXYAAMv1R.jpg", size: .init(width: 500, height: 616)),
                    FeedImage(url: "https://pbs.twimg.com/media/D-EHQDcWwAUImah.jpg", size: .init(width: 800, height: 592)),
                    FeedImage(url: "https://pbs.twimg.com/tweet_video_thumb/D-EZkIkXUAEQwHC.jpg", size: .init(width: 500, height: 280)),
                ])
            }
        }
    }
}

图像节控制器

class ImageSectionController: ListSectionController {
    var image: FeedImage!

    override func numberOfItems() -> Int {
        return 1
    }

    override func sizeForItem(at index: Int) -> CGSize {
        return .init(width: collectionContext!.containerSize.width, height: 1)
    }

    override func cellForItem(at index: Int) -> UICollectionViewCell {
        let cell = collectionContext?.dequeueReusableCell(of: CustomCellTwo.self, for: self, at: index) as! CustomCellTwo
        cell.render(model: image)
        return cell
    }

    override func didUpdate(to object: Any) {
        image = object as? FeedImage
    }
}

自定义CellTwo

class CustomCellTwo: UICollectionViewCell {
    private lazy var width: NSLayoutConstraint = {
        let width = contentView.widthAnchor.constraint(equalToConstant: bounds.size.width)
        width.isActive = true
        return width
    }()

    var imageBodyBottomAnchor: NSLayoutConstraint!
    var imageHeightAnchor: NSLayoutConstraint!

    let image = UIImageView(frame: .zero)
    lazy var imageHeight: CGFloat = 0

    override init(frame: CGRect) {
        super.init(frame: frame)
        imageHeightAnchor = image.heightAnchor.constraint(equalToConstant: 0)
        imageHeightAnchor.isActive = true
    }

    required init?(coder aDecoder: NSCoder) {
        return nil
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        image.kf.cancelDownloadTask()
    }

    override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
        width.constant = bounds.size.width
        return contentView.systemLayoutSizeFitting(CGSize(width: targetSize.width, height: 1))
    }

    func render(model: FeedImage) {

        image.translatesAutoresizingMaskIntoConstraints = false
        imageHeightAnchor.constant = model.heightForFeed
        image.kf.setImage(with: model.url)

        contentView.addSubview(image)

        NSLayoutConstraint.activate([
            image.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 16),
            image.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
            image.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16)
        ])

        if let lastSubview = contentView.subviews.last {
            imageBodyBottomAnchor = contentView.bottomAnchor.constraint(equalTo: lastSubview.bottomAnchor, constant: 0)
            imageBodyBottomAnchor.priority = .init(999)
            imageBodyBottomAnchor.isActive = true
        }
    }
}
4

0 回答 0