Я использую UIKit программно. У меня UITableView
отлично работает в другом контроллере представления, и я в значительной степени скопировал код, который я там использовал, в этот VC, но я не получаю никаких ошибок, а UITableView
не отображается. Я проверил иерархию представления отладки, и ее там тоже нет. Я не уверен, связано ли это, но при запуске иерархии представления отладки в окне отладки неоднократно отображается следующее сообщение:
[API] Failed to get renderer info (client=0x82b3d507) [0x5 (os/kern) failure]
. Мое утверждение print
внутри numberOfRowsInSection
срабатывает, а мое утверждение print
в cellForRowAt
— нет. WorkoutTemplate
и его дочерние структуры — это простые структуры данных без логики. У меня есть статическое свойство WorkoutTemplate, которое используется для фиктивных данных при макетировании пользовательского интерфейса.
class CreateWorkoutVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
var exerciseListing: UITableView!
var workoutTemplate: WorkoutTemplate!
init(workoutTemplate: WorkoutTemplate) {
super.init(nibName: nil, bundle: nil)
self.workoutTemplate = workoutTemplate
configure()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
}
private func configure() {
let padding: CGFloat = 20
exerciseListing = UITableView(frame: view.frame)
view.addSubview(exerciseListing)
exerciseListing.register(UITableViewCell.self, forCellReuseIdentifier: "exercise")
exerciseListing.rowHeight = 50
exerciseListing.dataSource = self
exerciseListing.delegate = self
exerciseListing.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
exerciseListing.topAnchor.constraint(equalTo: view.topAnchor, constant: padding),
exerciseListing.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: padding),
exerciseListing.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -padding)
])
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("In numberOfRowsInSection: \(workoutTemplate.exerciseSets.count)")
return workoutTemplate.exerciseSets.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "exercise", for: indexPath)
let cellLabel = getCellLabel(withText: workoutTemplate.exerciseSets[indexPath.row].exercise.exerciseName)
print(workoutTemplate.exerciseSets[indexPath.row].exercise.exerciseName)
cell.addSubview(cellLabel)
let padding: CGFloat = 8
NSLayoutConstraint.activate([
cellLabel.centerYAnchor.constraint(equalTo: cell.centerYAnchor),
cellLabel.leadingAnchor.constraint(equalTo: cell.leadingAnchor, constant: padding),
cellLabel.trailingAnchor.constraint(equalTo: cell.trailingAnchor, constant: -padding),
cellLabel.heightAnchor.constraint(equalTo: cell.heightAnchor)
])
cell.backgroundColor = .secondarySystemBackground
return cell
}
func getCellLabel(withText labelText: String) -> UILabel {
let cellLabel = UILabel(frame: .zero)
cellLabel.text = labelText
cellLabel.textColor = .label
cellLabel.adjustsFontSizeToFitWidth = true
cellLabel.minimumScaleFactor = 0.90
cellLabel.lineBreakMode = .byTruncatingTail
cellLabel.translatesAutoresizingMaskIntoConstraints = false
cellLabel.textAlignment = .left
cellLabel.font = UIFont.systemFont(ofSize: 16, weight: .bold)
return cellLabel
}
}
Структуры данных:
struct WorkoutTemplate {
let workoutName: String
var exerciseSets: [ExerciseSet]
mutating func addExerciseSet(exerciseSet: ExerciseSet) {
exerciseSets.append(exerciseSet)
}
static let exampleWorkouts = [WorkoutTemplate(workoutName: "Upper Body 1", exerciseSets: [ExerciseSet(exercise: Exercise(exerciseId: UUID(), exerciseName: "Bench Press"), sets: 3, targetReps: 8, targetRPE: 8, restPeriod: 2), ExerciseSet(exercise: Exercise(exerciseId: UUID(), exerciseName: "T-Bar Row"), sets: 3, targetReps: 8, targetRPE: 8, restPeriod: 3)]), WorkoutTemplate(workoutName: "Lower Body 1", exerciseSets: [ExerciseSet(exercise: Exercise(exerciseId: UUID(), exerciseName: "Hack Squats"), sets: 3, targetReps: 8, targetRPE: 8, restPeriod: 2), ExerciseSet(exercise: Exercise(exerciseId: UUID(), exerciseName: "Leg Ext"), sets: 3, targetReps: 8, targetRPE: 8, restPeriod: 3)])]
}
struct ExerciseSet {
let exercise: Exercise
let sets: Int
let targetReps: Int
let targetRPE: Int
let restPeriod: Int
}
struct Exercise {
let exerciseId: UUID
let exerciseName: String
}
Это не ваша непосредственная проблема, но вы добавляете еще одну метку и ее ограничения в tableView:cellForRowAtIndexPath:
. Этот метод будет вызываться для существующих ячеек, когда они повторно используются при прокрутке. Вы должны добавить метку и ограничения в класс ячеек, чтобы они случались только один раз.
@HangarRash Контроллер представления управляет не только UITableView. Также есть UILabels, UITextFields и UIButtons. Я удалил их здесь как для ясности, так и в рамках моего процесса отладки, чтобы увидеть, приведет ли их удаление к отображению TableView. К сожалению, их удаление не влияет на TableView.
Нет нижнего якоря/ограничения? Не могли бы вы переопределить layoutSubView
и распечатать там рамку вашего tabeView? cellForRowAt
не будет вызываться, если нет ячейки для отображения
@Rob Графическое представление, похожее на вращающийся каркас.
Вы также должны добавить метку к ячейке contentView
и использовать ограничения с представлением содержимого, а не добавлять метку непосредственно в ячейку.
@Роб Спасибо! Вы указали мне правильное направление. В иерархии представлений на левой панели был фиолетовый значок предупреждения, указывающий, что высота TableView неоднозначна. После установки ограничения heightAnchor TableView отобразился нормально.
Независимо от этого, я бы переместил инициализацию ячейки в подкласс ячейки, чтобы вы делали это только один раз. Например. gist.github.com/robertmryan/54833388ee0fa5a0107579fd4711419f.
@Rob Согласен, и я только что сделал это. Использование ванили UITableViewCell
без подклассов кажется не очень практичным. Но я новичок в этом, поэтому, возможно, есть вариант использования, о котором я не знаю.
Ванильный UITableViewCell
крайне удобен, у него уже есть метка (и, опционально, метка подзаголовка, и вид изображения) бесплатно. Это отлично подходит для быстрых, одноразовых вещей, когда вы еще не настроили свою собственную ячейку. Но как только мы приступаем к настройке пользовательских ячеек, мы создаем для этого собственные подклассы…
Вы сказали:
Я проверил иерархию представления отладки, и ее там тоже нет.
Вы проверили в графическом представлении? Или в иерархии на панели слева? Я спрашиваю, потому что вы, похоже, никогда не устанавливали нижнее ограничение, поэтому его высота может быть любой (даже нулевой). И это приведет к тому, что numberOfRowsInSection
будет вызвано, а cellForRowAt
не будет вызвано.
В итоге табличное представление было нулевой высоты, вы не увидели бы его в этом каркасе (или оно выглядело бы как горизонтальная линия). Вот почему я советую перемещаться по иерархии представлений на панели слева, чтобы найти табличное представление. Каркас отлично подходит для отладки размещения видов, которые вы можете видеть, но виды с нулевой высотой сложнее анализировать.
Покажите, как вы создаете экземпляр
CreateWorkoutVC
. А почему бы не использоватьUITableViewController
? Зачем расширятьUIViewController
табличным представлением?