问题
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