Querying Realm to populate numberOfRowsInSection and cellForRowAt with Multiple Sections using Realm and Swift

喜你入骨 提交于 2020-07-21 08:08:41

问题


I'm new to Realm and Swift, and to this site for that matter, so please forgive me if my question is poorly worded, but I will do my best. Here goes...

Basically I'm trying to build a Gym App. The idea is to allow the user to type out the title of their workout and to select a day of the week from a Picker View, to assign to that particular workout.

With that said, I'm having some trouble figuring out how to code the numberOfRowsInSection function so that it returns the number of rows based on the number of objects in that particular section. In other words, to return the number of rows based on the number of workouts that I have stored for that particular day of the week.

I'm also having a similar problem with the cellForRowAt function. I'm trying to figure out how to populate the cells with titles of the workouts, based on section/day of the week.

Any help would be greatly appreciated.

import UIKit
import RealmSwift
import SwipeCellKit

class WorkoutsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, SwipeTableViewCellDelegate {


    let realm = try! Realm()

    var workouts : Results<Workouts>?
    var days : Results<WeekDays>?

    var daysOfWeek : [String] = ["Monday", "Tuesday", "Wednsday", "Thursday", "Friday", "Saturday", "Sunday"]

    let picker = UIPickerView()


    @IBOutlet weak var WorkoutsTableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        WorkoutsTableView.delegate = self
        WorkoutsTableView.dataSource = self

        picker.delegate = self
        picker.dataSource = self

        loadCategories()
    }


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

        tableView.rowHeight = 80.0

        //Populate based on the # of workouts in each day.

        return 0
    }

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let button = UIButton(type: .system)
        button.setTitleColor(.black, for: .normal)
        button.backgroundColor = .lightGray
        button.setTitle(daysOfWeek[section], for: .normal)

        return button
    }


    func numberOfSections(in tableView: UITableView) -> Int {
        return 7
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! SwipeTableViewCell
        cell.accessoryType = .disclosureIndicator
        cell.delegate = self

        //Populate with titles of workouts based on section/day of the week.
        //cell.textLabel?.text = days?[indexPath.row].workouts[indexPath.row].name


        return cell
    }

    func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? {

        guard orientation == .right else { return nil }

        let deleteAction = SwipeAction(style: .destructive, title: "Delete") { action, indexPath in



        }

        // customize the action appearance
        deleteAction.image = UIImage(named: "delete-icon")

        return [deleteAction]
    }

    func tableView(_ tableView: UITableView, editActionsOptionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> SwipeOptions {
        var options = SwipeOptions()
        options.expansionStyle = .destructive
        return options
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
    }



    @IBAction func AddWorkoutButton(_ sender: UIButton) {
        var textField = UITextField()

        let alert = UIAlertController(title: "New Workout", message: "Please name your workout...", preferredStyle: .alert)

        let addAction = UIAlertAction(title: "Add Workout", style: .default) { (UIAlertAction) in
                //Add workout to database
            let newWorkout = Workouts()
            let dow = WeekDays()

            dow.day = self.daysOfWeek[self.picker.selectedRow(inComponent: 0)]
            newWorkout.name = textField.text!
            dow.workouts.append(newWorkout)

            self.save(newDay: dow)
        }

        alert.addTextField { (alertTextField) in
            alertTextField.placeholder = "Muscle Group"
            textField = alertTextField
            alertTextField.inputView = self.picker
        }

        alert.addAction(addAction)

        present(alert, animated: true, completion: nil)
    }

    func save(newDay: WeekDays){
        do {
            try realm.write {
                realm.add(newDay)
            }
        } catch {
            print("Error saving workout \(error)")
        }
        WorkoutsTableView.reloadData()
    }

    func loadCategories(){
        days = realm.objects(WeekDays.self)
        workouts = realm.objects(Workouts.self)
        WorkoutsTableView.reloadData()
    }

    @IBAction func EditWorkout(_ sender: UIBarButtonItem) {

    }

}

extension WorkoutsViewController : UIPickerViewDelegate, UIPickerViewDataSource {

    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return 7
    }

    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {

        return daysOfWeek[row]
    }



}

{

class Workouts : Object {
            @objc dynamic var name : String = ""
            var parentDay = LinkingObjects(fromType: WeekDays.self, property: "workouts")
        }




class WeekDays : Object {
    @objc dynamic var day : String = ""
    let workouts = List<Workouts>()
}

回答1:


Thank you for providing us with your models. As I see you already have a list of Workouts on your WeekDay elements, so your query to populate your Table View is simplified by this.

First things first. I recommend you to change your declaration of the results in your controller to use the following

class WorkoutsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, SwipeTableViewCellDelegate {
    let realm = try! Realm()
    var days : Results<WeekDays>!
    // ... rest of the implementation 
}

This way you can void the optional handling for the table view datasource and delegate methods.

That being said, you only need to query the WeekDay objects on the view controller. I usually do this on the viewDidLoad:

days = realm.objects(WeekDays.self)

The associated workouts are loaded automatically and associated to each of the days you're getting from the database, on the days array.

Based on your requirement you can create the required number of sections for your table view like the following:

func numberOfSections(in tableView: UITableView) -> Int {
    return self.days.count
}

That code will create as many section as the size of the days array. The next task is to provide the number of rows in the given section. This is almost immediate because we already have an array of the WorkOuts objects on the day:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    let day = days[section]
    return day.workouts.count
}

At this point we already have provided the number of sections in the table view (days) and the number of rows for each section (workouts associated to the corresponding day).

After this each cell can be configured based on the days array information (don't forget this is an array of WeekDays objects, each one containing an array of workouts.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! SwipeTableViewCell
    cell.accessoryType = .disclosureIndicator
    cell.delegate = self

    // Populate with titles of workouts based on section/day of the week.
    cell.textLabel?.text = days[indexPath.section].workouts[indexPath.row].name

    return cell
}

The key here is you must obtain the WeekDays object (by getting the object at the index indexPath.section from the days array) and then getting the Workout details by getting the Workouts object at index indexPath.row from the weekday's array.

Hope this helps!



来源:https://stackoverflow.com/questions/59061507/querying-realm-to-populate-numberofrowsinsection-and-cellforrowat-with-multiple

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!