Оболочка SwiftUI UICollectionView или UITableView и многократно используемые ячейки эффективны для замены содержимого ячейки новыми представлениями SwitUI - PullRequest
1 голос
/ 05 апреля 2020

Как мы знаем, при использовании UITableView или UICollectionView мы регистрируем повторно используемые ячейки классов, а затем мы снимаем с очереди эти многократно используемые ячейки , чтобы составить список / collection работает более эффективно с большим набором данных.

Можно ли использовать такие UITableViews или UICollectionViews с SwiftUI Content в любом подходе таким образом, чтобы не ухудшить эффективность этих списков / коллекций?

Я рассматриваю такие примеры:

  1. Ячейки UICollectionView

    private func cellProvider(collectionView: UICollectionView, indexPath: IndexPath, item: Item) -> UICollectionViewCell? {
    
        print("Providing cell for \(indexPath)")
    
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HostingControllerCollectionViewCell<AnyView>.reuseIdentifier, for: indexPath) as? HostingControllerCollectionViewCell<AnyView> else {
            fatalError("Coult not load cell!")
        }
    
        cell.host(content(indexPath, item))
    
        return cell
    }
    

    Класс HostingControllerCollectionViewCell: UICollectionViewCell {

    weak var controller: UIHostingController<Content>?
    
    func host(_ view: Content, parent: UIViewController? = nil) {
    
        if let controller = controller {
            controller.rootView = view
            controller.view.layoutIfNeeded()
        } else {
            let controller = UIHostingController(rootView: view)
            self.controller = controller
            controller.view.backgroundColor = .clear
    
            layoutIfNeeded()
    
            parent?.addChild(controller)
            contentView.addSubview(controller.view)
            controller.view.translatesAutoresizingMaskIntoConstraints = false
    
            NSLayoutConstraint.activate([
                controller.view.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor),
                controller.view.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor),
                controller.view.topAnchor.constraint(equalTo: self.contentView.topAnchor),
                controller.view.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor)
            ])
    
            if let parent = parent {
                controller.didMove(toParent: parent)
            }
            controller.view.layoutIfNeeded()
        }
    }
    

    }

  2. UITableViewCell

    класс HostingTableViewControllerCell: UITableViewCell {

    private weak var controller: UIHostingController<Content>?
    
    func host(_ view: Content, parent: UIViewController) {
        if let controller = controller {
            controller.rootView = view
            controller.view.layoutIfNeeded()
        } else {
            let swiftUICellViewController = UIHostingController(rootView: view)
            controller = swiftUICellViewController
            swiftUICellViewController.view.backgroundColor = .clear
    
            layoutIfNeeded()
    
            parent.addChild(swiftUICellViewController)
            contentView.addSubview(swiftUICellViewController.view)
            swiftUICellViewController.view.translatesAutoresizingMaskIntoConstraints = false
            contentView.addConstraint(NSLayoutConstraint(item: swiftUICellViewController.view!, attribute: NSLayoutConstraint.Attribute.leading, relatedBy: NSLayoutConstraint.Relation.equal, toItem: contentView, attribute: NSLayoutConstraint.Attribute.leading, multiplier: 1.0, constant: 0.0))
            contentView.addConstraint(NSLayoutConstraint(item: swiftUICellViewController.view!, attribute: NSLayoutConstraint.Attribute.trailing, relatedBy: NSLayoutConstraint.Relation.equal, toItem: contentView, attribute: NSLayoutConstraint.Attribute.trailing, multiplier: 1.0, constant: 0.0))
            contentView.addConstraint(NSLayoutConstraint(item: swiftUICellViewController.view!, attribute: NSLayoutConstraint.Attribute.top, relatedBy: NSLayoutConstraint.Relation.equal, toItem: contentView, attribute: NSLayoutConstraint.Attribute.top, multiplier: 1.0, constant: 0.0))
            contentView.addConstraint(NSLayoutConstraint(item: swiftUICellViewController.view!, attribute: NSLayoutConstraint.Attribute.bottom, relatedBy: NSLayoutConstraint.Relation.equal, toItem: contentView, attribute: NSLayoutConstraint.Attribute.bottom, multiplier: 1.0, constant: 0.0))
    
            swiftUICellViewController.didMove(toParent: parent)
            swiftUICellViewController.view.layoutIfNeeded()
        }
    }
    

    }

или у меня есть другие решения просто с заменой подпредставлений contentView

class HostingTableViewCell: UITableViewCell {

    func host<Content: View>(rootView: Content) {

        self.contentView.subviews.forEach {  $0.removeFromSuperview() }

        let view = UIHostingController(rootView: rootView).view
        view?.backgroundColor = .clear

        self.contentView.addSubview(view!)

        view!.preservesSuperviewLayoutMargins = true
        view!.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            view!.leadingAnchor.constraint(equalTo: self.contentView.layoutMarginsGuide.leadingAnchor),
            view!.trailingAnchor.constraint(equalTo: self.contentView.layoutMarginsGuide.trailingAnchor),
            view!.topAnchor.constraint(equalTo: self.contentView.layoutMarginsGuide.topAnchor),
            view!.bottomAnchor.constraint(equalTo: self.contentView.layoutMarginsGuide.bottomAnchor)
        ])

    }
}

Может быть, я должен зарегистрировать несколько HostingCells с указанным типом представления c и вместо замены всего представления попытаться каким-то образом setTitle () setImage () для этого представления содержимого SwiftUI.

 private func cellProvider(collectionView: UICollectionView, indexPath: IndexPath, item: Item) -> UICollectionViewCell? {

        print("Providing cell for \(indexPath)")
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HostingControllerCollectionViewCell<CustomView>.reuseIdentifier, for: indexPath) as? HostingControllerCollectionViewCell<CustomView> else {
            fatalError("Could not load cell!")
        }

        if cell.hostingController == nil {
            cell.host(Text("LOL"))
        } else { 
           cell.hostingController.rootView 
           cell.controller?.rootView?.setTitle("Text") 
        } 

        return cell
}

struct CustomView: View { 

   @State var text: String 

   func setTitle(_ text: String) { self.text = text } 
}

Но тут вопрос. Как я могу зарегистрировать несколько представлений контента SwiftUI, таких как CustomView1, CustomView2, CustomView3?

возможно во время ограничения добавления

CollectionView(reusableCells: [CustomView1.self, CustomView2.self],...) 

и затем в configureCollectionView () add

collectionView.register (HostingControllerCollectionViewCell.self, forCellWithReuseIdentifier: CustomViewX.reuseIdentifier) ​​

но лучше ли этот последний предложенный подход, и как я мог бы передать типы структур представления и сохранить их, а затем сохранить другой тип представления, не стирая тип в AnyView

ОБНОВЛЕНИЕ

Если я добавлю к текущему soluton 100k элементов, тогда приложение работает очень и очень медленно, представления не имеют правильной компоновки, и кажется, что sh очень часто. Таким образом, кажется, что клетки не используются должным образом.

static func generateCategories() -> [Item] {

        var items = [Item]()

        for i in 0..<100000 {
            items.append(.category(category: Category(id: "\(i)", title: "Category \(i)")) )
        }

        return items
    }
...