0

MVVM パターンを学習し、Snapkit を使用してプログラムですべてのビューを作成しようとしています。単純な tableView で構成されるハンバーガー メニューを作成していますが、カスタム ビューの tableView でビュー コントローラーのデリゲートとデータ ソースの参照が失われているという問題があります。UITableViewController も使用してみましたが、結果は同じです。これが私のコードです。

ビューモデル:

class SideMenuViewModel {

    let cellId = "SideMenuCellId"
    weak var delegate: SideMenuViewModelDelegate?
    private let cells: [SideMenuItemStruct] = [SideMenuItemStruct(type: .allDogs, title: "ALL DOGOS"),
                                           SideMenuItemStruct(type: .randomDog, title: "RANDOM DOGO")]

    init(delegate: SideMenuViewModelDelegate) {
        self.delegate = delegate
    }

    var numberOfRows: Int {
        return cells.count
    }

    func selectedMenuItem(indexPath: IndexPath) {
        switch SideMenuItemsEnum(rawValue: indexPath.row) {
        case .allDogs?:
            delegate?.selectedMenuItem(selectedItem:        SideMenuItemsEnum.allDogs)
        case .randomDog?:
            delegate?.selectedMenuItem(selectedItem: SideMenuItemsEnum.randomDog)
        default:
            print("error when choosing menu item")
        }
    }

    func cellForRow(_ tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as? SideMenuCell else {
            fatalError("could not deque Side menu cell")
        }

        cell.selectionStyle = .none
        cell.setUpCell(sideMenuItem: cells[indexPath.row])
        return cell
        }
}

意見:

class SideMenuView: UIView {

    var sideMenuTableView = UITableView()

    let sideMenuButton = UIButton(type: .system)

    weak var delegate: UITableViewDelegate? {
        get {
            return sideMenuTableView.delegate
        }
        set {
            sideMenuTableView.delegate = newValue
        }
    }

    weak var dataSource: UITableViewDataSource? {
        get {
            return sideMenuTableView.dataSource
        }
        set {
            sideMenuTableView.dataSource = newValue
        }
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        initUI()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func awakeFromNib() {
        super.awakeFromNib()
    }

    private func initUI() {
        addSubview(sideMenuButton)
        addSubview(sideMenuTableView)

        setUpSideMenuButton()
        setUpSideMenuTableView()
    }

    private func setUpSideMenuButton() {
        sideMenuButton.setTitle("DELEGATE", for: .normal)
        sideMenuButton.addTarget(self, action: #selector(buttonPrint), for: .touchUpInside)
        sideMenuButton.snp.makeConstraints { (make) in
            make.top.equalTo(self)
            make.centerX.equalTo(self)
        }
    }

    @objc func buttonPrint() {
        print("delegate: \(String(describing: sideMenuTableView.delegate)), data source: \(String(describing: sideMenuTableView.dataSource))")
    }

    private func setUpSideMenuTableView() {
        sideMenuTableView.snp.makeConstraints { (make) in
            make.top.equalTo(sideMenuButton.snp.bottom)
            make.bottom.equalTo(self)
            make.left.equalTo(self)
            make.right.equalTo(self)
        }
    }

}

そして私のView Controller:

class SideMenuController: UIViewController {

    fileprivate let viewModel: SideMenuViewModel

    fileprivate var sideMenuView: SideMenuView {
        return view as! SideMenuView
    }

    init(viewModel: SideMenuViewModel) {
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: nil)
    }

    override func loadView() {
        let sideMenuView = SideMenuView()
        sideMenuView.sideMenuTableView.delegate = self
        sideMenuView.sideMenuTableView.dataSource = self
        view = sideMenuView
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        sideMenuView.sideMenuTableView.register(SideMenuCell.self, forCellReuseIdentifier: viewModel.cellId)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

extension SideMenuController: UITableViewDelegate, UITableViewDataSource {

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return viewModel.numberOfRows
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        return viewModel.cellForRow(tableView, indexPath: indexPath)
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        viewModel.selectedMenuItem(indexPath: indexPath)
        print("awd")
    }

}

初期化後のシミュレーター

スクロール後のシミュレーター

DELEGATE ボタンをタップした結果

私はいくつかのチュートリアルから学んでおり、この問題はありませんでしたが、それらはすべてインターフェイス ビルダーを使用していたため、これは避けたいと考えています。本当に間違ったことをしている場合は、お知らせください。ありがとうございます。

解決

私は、この表示されたコードの外で本当に大きな間違いを犯したことを知りました.関数でSideMenuControllerを初期化し、それへの参照を保持しなかったため、当然、関数の終了後に自動的に初期化が解除されました. それは本当に悪い間違いでした。すべての回答に感謝します。ここのコードは機能していますが、回答に従ってリファクタリングしました。

4

1 に答える 1

0

しばらくの間これをハッキングしていると思いますが、コードがあちこちに散らばっているようです。

MVVM に従う場合は、各コンポーネントの役割について考える必要があります。

  • モデル - の配列SideMenuItem
  • ViewModel - この場合、モデルと同じであるため、モデルを省略して ViewModel のみを使用できます。より複雑な例では、ViewModel がモデルにマップされ、ビューに必要なデータが公開され、必要な変換が実行されます。
  • ビュー - 実際の視覚要素。この場合、単なるテーブルビューです (ただし、デバッグ用のボタンもあります)。

  • 最後に、すべてをまとめるView Controllerがまだあります

ビューモデル

struct SideMenuViewModel {
    let items = [SideMenuItemStruct(type: .allDogs, title: "ALL DOGOS"),                                           
                 SideMenuItemStruct(type: .randomDog, title: "RANDOM DOGO")]
}

意見

class SideMenuView: UIView { 

    weak var viewModel: SideMenuViewModel?
    weak var delegate: SideMenuViewDelegate? // Was SideMenuViewModelDelegate

    private let sideMenuButton = UIButton(type: .system)
    private var sideMenuTableView = UITableView()
    private let cellId = "YourCellID"

    override init(frame: CGRect) {
        super.init(frame: frame)
        initUI()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func awakeFromNib() {
        super.awakeFromNib()
    }

    private func initUI() {
        addSubview(sideMenuButton)
        addSubview(sideMenuTableView)

        setUpSideMenuButton()
        setUpSideMenuTableView()
    }

    private func setUpSideMenuButton() {
        sideMenuButton.setTitle("DELEGATE", for: .normal)
        sideMenuButton.addTarget(self, action: #selector(buttonPrint), for: .touchUpInside)
        sideMenuButton.snp.makeConstraints { (make) in
            make.top.equalTo(self)
            make.centerX.equalTo(self)
        }
    }

    @objc func buttonPrint() {
        print("delegate: \(String(describing: sideMenuTableView.delegate)), data source: \(String(describing: sideMenuTableView.dataSource))")
    }

    private func setUpSideMenuTableView() {
        sideMenuTableView.snp.makeConstraints { (make) in
            make.top.equalTo(sideMenuButton.snp.bottom)
            make.bottom.equalTo(self)
            make.left.equalTo(self)
            make.right.equalTo(self)
        }
        sideMenuTableView.datasource = self
        sideMenuTableView.delegate = self
        sideMenuTableView.register(SideMenuCell.self, forCellReuseIdentifier: cellId)
    }

}

extension SideMenuView: UITableViewDelegate, UITableViewDataSource {

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return viewModel?.numberOfRows ?? 0
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as? SideMenuCell else {
            fatalError("could not deque Side menu cell")
        }

        cell.selectionStyle = .none
        cell.setUpCell(sideMenuItem: self.viewModel!.items[indexPath.row])
        return cell

    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let menuItem = self.viewModel!.items[indexPath.row]
        self.delegate?.didSelect(menuItem)
    }
}

ViewController

class SideMenuController: UIViewController {

    fileprivate let viewModel: SideMenuViewModel

    fileprivate var sideMenuView: SideMenuView {
        return view as! SideMenuView
    }

    override func loadView() {
        let sideMenuView = SideMenuView()
        sideMenuView.delegate = self
        sideMenuView.viewModel = viewModel
        view = sideMenuView
    }

    init(viewModel: SideMenuViewModel) {
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

extension SideMenuController: SideMenuViewDelegate {


    // TODO: Implement delegate method for menu selection


}
于 2018-07-05T20:41:39.317 に答える