UITableView не отображается в программном UIKit ViewController

Я использую 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
}

Покажите, как вы создаете экземпляр CreateWorkoutVC. А почему бы не использовать UITableViewController? Зачем расширять UIViewController табличным представлением?

HangarRash 13.04.2023 18:16

Это не ваша непосредственная проблема, но вы добавляете еще одну метку и ее ограничения в tableView:cellForRowAtIndexPath:. Этот метод будет вызываться для существующих ячеек, когда они повторно используются при прокрутке. Вы должны добавить метку и ограничения в класс ячеек, чтобы они случались только один раз.

Geoff Hackworth 13.04.2023 18:18

@HangarRash Контроллер представления управляет не только UITableView. Также есть UILabels, UITextFields и UIButtons. Я удалил их здесь как для ясности, так и в рамках моего процесса отладки, чтобы увидеть, приведет ли их удаление к отображению TableView. К сожалению, их удаление не влияет на TableView.

Legion 13.04.2023 18:20

Нет нижнего якоря/ограничения? Не могли бы вы переопределить layoutSubView и распечатать там рамку вашего tabeView? cellForRowAt не будет вызываться, если нет ячейки для отображения

Larme 13.04.2023 18:21

@Rob Графическое представление, похожее на вращающийся каркас.

Legion 13.04.2023 18:22

Вы также должны добавить метку к ячейке contentView и использовать ограничения с представлением содержимого, а не добавлять метку непосредственно в ячейку.

Geoff Hackworth 13.04.2023 18:23

@Роб Спасибо! Вы указали мне правильное направление. В иерархии представлений на левой панели был фиолетовый значок предупреждения, указывающий, что высота TableView неоднозначна. После установки ограничения heightAnchor TableView отобразился нормально.

Legion 13.04.2023 18:28

Независимо от этого, я бы переместил инициализацию ячейки в подкласс ячейки, чтобы вы делали это только один раз. Например. gist.github.com/robertmryan/54833388ee0fa5a0107579fd4711419f‌​.

Rob 13.04.2023 19:57

@Rob Согласен, и я только что сделал это. Использование ванили UITableViewCell без подклассов кажется не очень практичным. Но я новичок в этом, поэтому, возможно, есть вариант использования, о котором я не знаю.

Legion 13.04.2023 20:59

Ванильный UITableViewCell крайне удобен, у него уже есть метка (и, опционально, метка подзаголовка, и вид изображения) бесплатно. Это отлично подходит для быстрых, одноразовых вещей, когда вы еще не настроили свою собственную ячейку. Но как только мы приступаем к настройке пользовательских ячеек, мы создаем для этого собственные подклассы…

Rob 14.04.2023 22:45
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
10
74
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы сказали:

Я проверил иерархию представления отладки, и ее там тоже нет.

Вы проверили в графическом представлении? Или в иерархии на панели слева? Я спрашиваю, потому что вы, похоже, никогда не устанавливали нижнее ограничение, поэтому его высота может быть любой (даже нулевой). И это приведет к тому, что numberOfRowsInSection будет вызвано, а cellForRowAt не будет вызвано.

В итоге табличное представление было нулевой высоты, вы не увидели бы его в этом каркасе (или оно выглядело бы как горизонтальная линия). Вот почему я советую перемещаться по иерархии представлений на панели слева, чтобы найти табличное представление. Каркас отлично подходит для отладки размещения видов, которые вы можете видеть, но виды с нулевой высотой сложнее анализировать.

Другие вопросы по теме