更新超类的表视图

时间:2018-10-18 06:40:12

标签: ios swift uitableview

我按照教程制作了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
        }
    }
}

我没有在MyProfileController中定义UITableViewDataSource,而是创建了一个称为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()
////////////////
    }
}

我正在cellForRowAt indexPath method内为UITextView设置一个委托,以便在用户在textview中键入内容时调用textViewDidChange委托方法。到此为止有效。问题是我无法从此处更新tableView。如何更新MyProfileController的tablView?

3 个答案:

答案 0 :(得分:2)

您可以使用闭包将消息发送到表视图控制器。

在数据源对象中声明一个关闭变量。

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.

        }

    }

}

答案 1 :(得分:1)

有很多方法可以做到这一点。而且这取决于您团队的编码模式规则或我们应如何称呼它。

但这是我通常要做的:视图模型具有用于reloadingData的协议。或者更好的是,我所有视图模型的协议都有一个用于此类reloadData的基类,如下所示:

--from-code=utf-8

然后是 BaseViewModelDelegate

protocol ProfileDelegate: BaseViewModelDelegate {

}

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

如您所见,有一个/// 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?) } 方法。这就是我在需要时重新加载控制器的tableView的地方。但是同样,有很多方法可以做到这一点。我希望这会有所帮助!

答案 2 :(得分:1)

您可以将DataSource移出视图控制器,但是遵循正确的分隔很重要,我建议采用这种方法,因为它可以帮助您进行测试。

使用协议定义视图模型的行为(为了进行测试,您可以具有实现此协议的模拟视图模型):

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

然后用数据实现viewModel:

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 
            // ...
        }
    }
}