-3

How can I add SwiftUI on a UIView which is Inside of a UITableViewCell, usually with uihostingcontroller SwiftUI view can be added to a ViewController as child but how can we do this on a UITableViewCell

Sadman Samee
  • 107
  • 3
  • 7

2 Answers2

1

Uses @Binding to update the state

import SwiftUI
import UIKit

class TableTestVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
    @IBOutlet weak var tableView: UITableView!
    var count = 50

    let demo = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.register(TableTestCell.self, forCellReuseIdentifier: "TableTestCell")
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let range = (0 ... demo.count).randomElement() ?? 0
        let cell = tableView.dequeueReusableCell(withIdentifier: "TableTestCell") as! TableTestCell
        cell.label = String(demo.prefix(range))
        return cell
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return count
    }
}

class TableTestCell: UITableViewCell {
    lazy var view = TableTestCellView(label: self.labelBinder)
    var label: String = "none"

    lazy var labelBinder = Binding { self.label } set: { self.label = $0 }

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        let sub = UIHostingController(rootView: view).view!
        contentView.addSubview(sub)
        sub.translatesAutoresizingMaskIntoConstraints = false
        sub.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
        sub.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
        sub.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
        sub.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

struct TableTestCellView: View {
    @Binding var label: String

    var body: some View {
        Text(label)
            .padding()
            .multilineTextAlignment(.leading)
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        TableTestCellView(label: .constant("Testing\nHello world"))
    }
}
ChanOnly123
  • 1,004
  • 10
  • 12
0

I was able to get something to work like this:

import UIKit
import SwiftUI
import PlaygroundSupport

let cellIdentifier = "Swift Content"

struct CellContent : View {
    let title : String

    init(title: String) {
        self.title = title
    }

    var body : some View {
        Text(title)
    }
}

class SwiftUITableCell : UITableViewCell {
    var swiftContent = UIHostingController(rootView: CellContent(title: "Placeholder}"))
}

let tableView = UITableView(frame: CGRect(x: 0,y: 0,width: 320,height: 480))
let dataSource = UITableViewDiffableDataSource<Int,String>(tableView: tableView) {
    (tableView: UITableView, indexPath: IndexPath, itemIdentifier: String) -> UITableViewCell? in
    let newCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as!SwiftUITableCell

    newCell.swiftContent = UIHostingController.init(rootView: CellContent(title: itemIdentifier))
    newCell.swiftContent.view.frame = newCell.contentView.bounds.inset(by: UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 4))
    newCell.contentView.addSubview(newCell.swiftContent.view)

    return newCell
}

tableView.register(SwiftUITableCell.self, forCellReuseIdentifier: cellIdentifier)
tableView.dataSource = dataSource

var currentSnapshot = NSDiffableDataSourceSnapshot<Int, String>()
currentSnapshot.appendSections([0])
currentSnapshot.appendItems(["Foo", "Bar", "Baz"], toSection: 0)
dataSource.apply(currentSnapshot)

PlaygroundSupport.PlaygroundPage.current.liveView = tableView

To make this more "real" you would have to set up your swift view to handle all the various configuration changes that can happen to a UITableViewCell.

Scott Thompson
  • 22,629
  • 4
  • 32
  • 34