Программно созданный UITableView с несамостоятельным источником данных и делегатами

Я хотел бы использовать экземпляр отдельного класса в качестве источника данных и делегата моего UITableView. Я создаю новый экземпляр класса, который должен обрабатывать эту роль, и назначаю ее через свойства моего табличного представления:

autoMakesTable определяется как свойство:

var autoMakesTable : UITableView?

Дальше:

    func nextBttnTouched(sender: AnyObject) {

    switch self.autoState {

    case .NewOrEdit:

        self.autoState = .YearEntered

        // If the picker wasn't used create a new Auto with the default year value
        self.auto = Auto(year: NSNumber(integerLiteral: self.yearPickerValues.count - 2), make: "", model: "", body: "", trim: "", downPaymentBudget: 0, monthlyPaymentBudget: 0)

        // Prepare the make & model fields
        backgroundFieldView                     = UIView(frame: CGRectMake(0, 0-self.view.frame.size.height/2, self.view.frame.width, self.view.frame.height/2))
        backgroundFieldView!.backgroundColor    = UIColor(red: 251/255, green: 251/255, blue: 251/255, alpha: 1.0)

        let makeField           = UITextField(frame: CGRectMake(self.view.center.x - 100, self.navigationController!.navigationBar.frame.size.height + 34, 200, 30))
        makeField.delegate      = self
        makeField.placeholder   = "Make"
        makeField.addTarget(self, action: Selector("makeTextChanged:"), forControlEvents: UIControlEvents.EditingChanged)

        let modelField = UITextField(frame: CGRectMake(self.view.center.x - 100, makeField.frame.origin.y + makeField.frame.size.height + 8, 200, 30))
        modelField.placeholder = "Model"

        let newAutoMakesTableData = AutoMakesTableData()

        autoMakesTable = UITableView(frame: CGRectMake(makeField.frame.origin.x, makeField.frame.origin.y + makeField.frame.height + 2, makeField.frame.width, 100))
        autoMakesTable!.dataSource    = newAutoMakesTableData
        autoMakesTable!.delegate      = newAutoMakesTableData
        self.backgroundFieldView?.addSubview(self.autoMakesTable!)
        self.autoMakesTable!.reloadData()

        makeField.becomeFirstResponder()

        self.addBorderToTextField([makeField,modelField])

        self.view.addSubview(backgroundFieldView!)

        // Move year picker with animation
        // Update next button location while loading make / model input fields with animation
        UIView.animateWithDuration(0.8, animations: { () -> Void in

            self.newYearPicker?.frame.origin.y = 0
            self.backgroundFieldView!.frame.origin.y = 0
            self.backgroundFieldView!.addSubview(makeField)
            self.backgroundFieldView?.addSubview(modelField)
//self.backgroundFieldView?.addSubview(self.autoMakesTable!)
//                self.autoMakesTable!.reloadData()
        })

    case .YearEntered:
        self.autoState = .MakeModelEntered

    case .MakeModelEntered:
        self.autoState = .DetailsEntered

    case .DetailsEntered:
        self.autoState = .BudgetEntered
    case .BudgetEntered:
        performSegueWithIdentifier("next", sender: self)

    }

}

Позже он добавляется в иерархию представлений (я вижу пустое табличное представление с несколькими строками). Я также пытаюсь вызвать для него reloadData() безрезультатно.

AutoMakesTableData определяется так:

import UIKit

class AutoMakesTableData: NSObject, UITableViewDataSource, UITableViewDelegate {

// Auto makes URL
// This should be hosted online so changes can be made w/out having to re-submit the app
let urlForMakes = NSBundle.mainBundle().URLForResource("makes", withExtension: "JSON")
var autoMakes : [String]?

override init() {
    super.init()
    self.populateAutoMakesFromURL(urlForMakes!)

}

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

    return 1
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let aCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)

    print("cell created")

    return aCell
}

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    print("selected row")
}

private func populateAutoMakesFromURL(url: NSURL) {

    do {

        let jsonAutoMakesData = try NSData(contentsOfURL: url, options: NSDataReadingOptions.DataReadingMappedIfSafe)

        do {

            let jsonAutoMakes = try NSJSONSerialization.JSONObjectWithData(jsonAutoMakesData, options: NSJSONReadingOptions.MutableContainers) as! [String:[String]]

            self.autoMakes = jsonAutoMakes["makes"]
            // Sort array alphabetically
            self.autoMakes?.sortInPlace(<)

        } catch let error as NSError {

            print(error.userInfo.debugDescription)
        }

    } catch let error as NSError {

        print(error.userInfo.debugDescription)

    }

}

}

Я могу без проблем получить доступ к свойству массива autoMakes в этом экземпляре.

Отображается табличное представление, но ни один из операторов печати не срабатывает (для ячейки по индексному пути или для выбранной строки).


person Fred Faust    schedule 16.09.2015    source источник


Ответы (1)


Я нигде не вижу, где вы устанавливаете табличное представление в иерархию представлений. Если вы не установите контроллер представления в иерархию активного представления, он ничего не сделает.

Это мое предположение о том, что происходит не так.

Можно использовать отдельный объект в качестве источника данных/делегата контроллера представления.

РЕДАКТИРОВАТЬ:

Основываясь на обсуждении ниже этого ответа, выясняется, что проблема заключалась в том, что OP создавала свои AutoMakesTableData в методе и назначала их локальной переменной, но не сохраняла какие-либо другие сильные ссылки на нее.

Когда метод, создавший объект AutoMakesTableData, вернулся, локальная переменная вышла за пределы области видимости, сильных ссылок на объект больше не было, и он был освобожден. Свойства делегата табличного представления и dataSource являются слабыми, поэтому они обнуляются при освобождении объекта. Табличное представление больше не имеет источника данных, поэтому ничего не происходит.

person Duncan C    schedule 16.09.2015
comment
Извините, он установлен позже в файле, обновляя вопрос сейчас. - person Fred Faust; 16.09.2015
comment
Также покажите контекст, в котором вы создаете переменную newAutoMakesTableData. Кому-то нужна сильная ссылка на этот объект, иначе он будет освобожден. Ваш пост выглядит так, как будто это локальная переменная, а это означает, что, как только она выйдет за пределы области видимости, она будет освобождена, а свойства источника данных и делегата табличного представления будут установлены на ноль (они слабые ссылки). - person Duncan C; 16.09.2015
comment
Я добавил контекст кода. Я также пробовал это вне закрытия анимации (чуть выше него) безрезультатно. - person Fred Faust; 16.09.2015
comment
Покажите весь метод, который определяет newAutoMakesTableData, и расскажите нам, в каком классе определен этот метод. Мне кажется, что строка let newAutoMakesTableData = AutoMakesTableData() определяет newAutoMakesTableData как локальную переменную, но вы никогда не присваиваете ее сильному свойству или переменной экземпляра. Если это правда, как только этот метод вернется, newAutoMakesTableData будет освобожден. - person Duncan C; 16.09.2015
comment
Вот именно, спасибо. Как только я создал свойство экземпляра для newAutoMakesTableData и присвоил этому свойству свойства tableview, все заработало. - person Fred Faust; 16.09.2015
comment
Ага. Вывод заключается в том, что каждый объект, которым вы хотите жить, должен принадлежать кому-то, кто остается рядом. Если вы создаете объект, и единственной строгой ссылкой является локальная переменная, объект исчезнет, ​​как только вы покинете область действия этой локальной переменной, и сильная ссылка будет освобождена. - person Duncan C; 16.09.2015
comment
Спасибо - как только узнал, что такое сильный реф. цикл был Я всегда пытался работать наизнанку, опасаясь, что два объекта будут заперты в существовании, но это имеет смысл в этом мире. - person Fred Faust; 16.09.2015
comment
Делегаты являются слабыми ссылками, чтобы избежать циклов ссылок. В противном случае контроллер представления будет владеть табличным представлением, а табличное представление будет владеть своим делегатом, и вы получите цикл сохранения. Основное правило заключается в том, что в ситуации, когда 2 объекта указывают друг на друга, одна из этих ссылок должна быть слабой. - person Duncan C; 16.09.2015