简体   繁体   中英

TableView with Diffable DataSource on MacOs

I am writing an application using MacOs Big Sur with Xcode 12.2.

I am trying to implement a table view for a contact list (from examples on the internet).

Most of the code is for IOS en hard to port to MacOS.

For the most functionalities, the app is working very well.

I cannot succeed in implementing headers/footers for sections.

I did succeed with a previous project built around CollectionView.

But I cannot see the analogy between CollectionView and TableView. In CollectionView I used a nib for header/footers.

Here are the pieces of code for the Diffable Datasource example.

I hope somebody can help solving my problem.

'''
override func viewDidLoad() {
   tableView.delegate = self
   DataSource = makeDataSource()        
   tableView.dataSource = DataSource
   update(with:ContactList(all: Contact.all, 
          friends: [], 
          family: [], 
          coworkers: []),    
          animate: true)
}


func makeDataSource() -> NSTableViewDiffableDataSource<Section, Contact> {
    let reuseIdentifier = ContactTableCell.reuseIdentifier
    return NSTableViewDiffableDataSource( tableView: tableView, cellProvider: { tableView, column, indexPath, contact  in
        guard let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(reuseIdentifier), owner: self) as? ContactTableCell else {
            print("Failed to create results cell")
            return NSView()
        }
        cell.configure(with: contact)
        return cell
    }
    )
}

func update(with list: ContactList, animate: Bool = true) {
    var snapshot = NSDiffableDataSourceSnapshot<Section, Contact>()
    snapshot.appendSections([Section.all, Section.family,Section.coworkers,Section.friends])
    
    snapshot.appendItems(list.all, toSection: .all)
    snapshot.appendItems(list.family, toSection: .family)
    snapshot.appendItems(list.coworkers, toSection: .coworkers)
    snapshot.appendItems(list.friends, toSection: .friends)
    
    DataSource.apply(snapshot, animatingDifferences: animate)
}

class ContactTableCell: NSTableCellView{

    @IBOutlet weak var firstname: NSTextField!
    @IBOutlet weak var lastname: NSTextField!
    @IBOutlet weak var email: NSTextField!
    @IBOutlet weak var userView: NSImageView!

    static var reuseIdentifier: String {
        return NSUserInterfaceItemIdentifier(String(describing: ContactTableCell.self)).rawValue
    }

    func configure(with contact:Contact){
        print("\(#function)")
        self.userView.imageScaling = .scaleProportionallyDown
        self.userView.image = NSImage(contentsOfFile:contact.imagePath)
        self.firstname.stringValue = contact.firstName //"\(contact.firstName) \(contact.lastName)"
        self.lastname.stringValue = contact.lastName  //contact.emailAddress
        self.email.stringValue = contact.emailAddress
    }
}

class SectionHeaderView: NSTableHeaderView {
static var reuseIdentifier: String {
    return NSUserInterfaceItemIdentifier(String(describing: SectionHeaderView.self)).rawValue
}

lazy var headerLabel: NSTextField = {
    print("\(#function)")
    let label = NSTextField()
    label.textColor = .red
    label.font = NSFont.systemFont(ofSize: 15, weight: .medium)
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
}()
    
override  func draw(_ dirtyRect: NSRect) {
    print("\(#function)")
    super.draw(dirtyRect)
    // Drawing code here.
}

required  init?(coder: NSCoder) {
    print("\(#function)")
    super.init(coder: coder)
    setupView()
}

func setupView() {
    print("\(#function)")
    addSubview(headerLabel)
    setupLayout()
}

func setupLayout() {
    print("\(#function)")
    NSLayoutConstraint.activate([
        headerLabel.topAnchor.constraint(equalTo: self.topAnchor ,constant: 2),
        headerLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, 
                                            constant: -2),
        headerLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor,
                                             constant: 8),
        headerLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor,
                                            constant: -8),
    ])
}

} '''

A table view in macOS works a bit different, because there are no explicit sections like in iOS. The section headers are Group Rows which are inline as a part of the data source array.

To display section headers in the diffable data source create a struct rather than an enum

struct Section : Hashable {
    let name : String
}

Extend makeDataSource()

func makeDataSource() -> NSTableViewDiffableDataSource<Section, Contact> 
{
    let reuseIdentifier = ContactTableCell.reuseIdentifier
    let dataSource = NSTableViewDiffableDataSource<Section,Contact>( tableView: tableView, cellProvider: { tableView, column, row, contact  in
        let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(reuseIdentifier), owner: self) as! ContactTableCell
        cell.configure(with: contact)
        return cell
    })

    dataSource.sectionHeaderViewProvider = { tableView, row, section in
        let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "group"), owner: self) as! NSTableCellView
        cell.textField?.stringValue = section.name
        return cell
    }
    return dataSource
}

and populate the data source

func update(with list: ContactList, animate: Bool = true) {
    var snapshot = NSDiffableDataSourceSnapshot<Section, Contact>()
    let sections = [Section(name: "all"), Section(name: "family"), Section(name: "coworkers"), Section(name: "friends")

    snapshot.appendSections(sections)
    snapshot.appendItems(list.all, toSection: sections[0])
    snapshot.appendItems(list.family, toSection: sections[1])
    snapshot.appendItems(list.coworkers, toSection: sections[2])
    snapshot.appendItems(list.friends, toSection: sections[3])
    DataSource.apply(snapshot, animatingDifferences: animate)
}

I ran into a similar problem, and looking at the source code, what might need to be done in your case is set these to your diffableDataSource:

@property (copy,nullable) NSTableViewDiffableDataSourceRowProvider rowViewProvider;

@property (copy,nullable) NSTableViewDiffableDataSourceSectionHeaderViewProvider sectionHeaderViewProvider;

I don't think there is a section footer API, so you might need to show some sort of footer on the last cell item in the section.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM