Обновление табличного представления суперкласса - PullRequest
0 голосов
/ 18 октября 2018

Я следовал учебному пособию, чтобы создать табличное представление модели MVVP

Мой tableViewController называется MyProfileController и выглядит следующим образом:

class MyProfileController: UITableViewController {

    fileprivate var viewModel: ProfileViewModel?
    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.register(UserInfoCell.self, forCellReuseIdentifier: UserInfoCell.identifier)
        viewModel = ProfileViewModel()
        tableView.dataSource = self.viewModel
        }
    }
}

Вместо определения UITableViewDataSource в MyProfileController, я создаю модель представленияназывается ProfileViewModel и передать его tableView.dataSource.ProfileViewModel определяется следующим образом:

class ProfileViewModel: NSObject {
    fileprivate var profile: UserProfile?
    var items = [ProfileViewModelItem]()

    init() {
        super.init()
        //...
    }
}

extension ProfileViewModel: UITableViewDataSource {

    // ...

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! UserInfoCell
        cell.userDetailTextView.delegate = self
        return cell
    }

    // ...
}

extension ProfileViewModel: UITextViewDelegate {
    func textViewDidChange(_ textView: UITextView) {
        print(textView.text)

////////////////
// ERROR HERE //
//        tableView.beginUpdates()
//        tableView.endUpdates()
////////////////
    }
}

Я устанавливаю делегат для UITextView внутри cellForRowAt indexPath method, чтобы метод делегата textViewDidChange вызывался, когда пользователь вводит текстовое представление.До этого момента работает.Проблема в том, что я не могу обновить tableView отсюда.Как я могу обновить tablView MyProfileController?

Ответы [ 3 ]

0 голосов
/ 18 октября 2018

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

Объявите переменную замыкания в объекте источника данных.

class ProfileViewModel: NSObject {

    var textViewDidChange: (() -> Void)?

    // If you need to send some data to your controller, declare it with type. In your case it's string.
    // var textViewDidChange: ((String) -> Void)?

}

Отправьте ваше сообщение от вашего делегата текстового поля наваша вновь созданная переменная, подобная этой.

func textViewDidChange(_ textView: UITextView) {

    self.textViewDidChange?()
    // If you need to send your string, do it like this
    // self.textViewDidChange?(textView.text)

}

Как вы можете догадаться, ваша переменная textViewDidChange по-прежнему nil, поэтому никакое сообщение не пройдет.Поэтому мы должны объявить это сейчас.

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

class MyProfileController: UITableViewController {

    fileprivate var viewModel: ProfileViewModel?
    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.register(UserInfoCell.self, forCellReuseIdentifier: UserInfoCell.identifier)
        viewModel = ProfileViewModel()
        tableView.dataSource = self.viewModel

        // Set the closure value here

        viewmodel.textViewDidChange = { [unowned self](/* if you are sending data, capture it here, if not leave empty */) in

            // Do whatever you like with your table view here.
            // [unowned self] could sound tricky. It's whole another subject which isn't in the scope of this question. But of course there are some great answers here about it. Simply put, if you don't use it, you'll create a memory leak.

        }

    }

}
0 голосов
/ 18 октября 2018

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

Использование протокола для определения поведениявашей модели представления (для тестирования у вас может быть модель фиктивного представления, которая реализует этот протокол):

protocol ProfileViewModelType {
    var items: [ProfileViewModelItem]
    var textViewDidChange: ((UITextView) -> Void)?)
    //...
}

Затем реализуйте вашу модель представления с данными:

class ProfileVieModel: ProfileViewModelType {
    var items = [ProfileViewModelItem]()
    //...
}

Затем введите в свойDataSource возбуждает модель представления и использует ее для заполнения вашего табличного представления и для управления всеми обратными вызовами:

class ProfileTableViewDataSource: NSObject, UITableViewDataSource {

    private var viewModel: ProfileViewModelType!

    init(viewModel: ProfileViewModelType) {
        self.viewModel = viewModel
    }

    func textViewDidChange(_ textView: UITextView) {
        print(textView.text)
        viewModel.textViewDidChange?(textView)
    }
}

Наконец, в вашем контроллере представления вы можете наблюдать обратные вызовы модели представления и управлять своими действиями:

class YourViewController: UIViewController {
    private var dataSource: ProfileTableViewDataSource?
    private var viewModel: ProfileViewModelType = ProfileViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()
        dataSource = ProfileTableViewDataSource(viewModel: viewModel)
        tableView.dataSource = dataSource 
        bindViewModel()
    }

    func bindViewModel() {
        viewModel.textViewDidChange = { [weak self] textView in 
            // ...
        }
    }
}
0 голосов
/ 18 октября 2018

Есть много способов сделать это.И это зависит от правил шаблона кодирования вашей команды или от того, как мы должны это называть.

Но это то, что я обычно делаю: модель представления имеет протокол для перезагрузки данных.Или, что еще лучше, протокол всех моих моделей представлений имеет базовый класс для таких reloadData, например:

protocol ProfileDelegate: BaseViewModelDelegate {

}

class ProfileViewModel: NSObject {
    //....
}

И здесь идет BaseViewModelDelegate :

/// The Base Delegate of all ViewModels.
protocol BaseViewModelDelegate: NSObjectProtocol {
    /// Ask the controller to reload the data.
    func reloadTableView()
    /// Presents an alert/
    func presentAlert(title: String, message: String, okayButtonTitle: String, cancelButtonTitle: String?, withBlock completion: LLFAlertCallBack?)
}

Как видите, есть метод reloadTableView.И вот где я перезагружаю tableView моих контроллеров, если это необходимо.Но опять же, есть много способов сделать это.Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...