添加编程视图而不是情节提要后出现意外的nil

时间:2019-03-17 18:02:17

标签: ios swift mvvm tableview rx-swift

我有一个MVVM-C模式的应用程序,使用RxSwift

以编程方式添加新视图后,应用程序因

崩溃
  

线程1:致命错误:展开包装时意外发现nil   可选值

错误。我完全不知所措,实现几乎完全相同,减去了一个视图控制器是一个故事板,而另一个不是的情况。

这是我的新ViewController

import UIKit
import RxSwift
import RxCocoa

final class FeedViewController: TableViewController, ViewModelAttaching {

    var viewModel: Attachable<FeedViewModel>!
    var bindings: FeedViewModel.Bindings {
        let viewWillAppear = rx.sentMessage(#selector(UIViewController.viewWillAppear(_:)))
            .mapToVoid()
            .asDriverOnErrorJustComplete()

        let refresh = tableView.refreshControl!.rx
            .controlEvent(.valueChanged)
            .asDriver()

        return FeedViewModel.Bindings(
            fetchTrigger: Driver.merge(viewWillAppear, refresh),
            selection: tableView.rx.itemSelected.asDriver()
        )
    }

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

    func bind(viewModel: FeedViewModel) -> FeedViewModel {
        viewModel.posts
            .drive(tableView.rx.items(cellIdentifier: FeedTableViewCell.reuseID, cellType: FeedTableViewCell.self)) { _, viewModel, cell in
                cell.bind(to: viewModel)
            }
            .disposed(by: disposeBag)

        viewModel.fetching
            .drive(tableView.refreshControl!.rx.isRefreshing)
            .disposed(by: disposeBag)

        viewModel.errors
            .delay(0.1)
            .map { $0.localizedDescription }
            .drive(errorAlert)
            .disposed(by: disposeBag)

        return viewModel
    }

}

这是现有的,可以使用情节提要

final class PostsListViewController: TableViewController, ViewModelAttaching {

    var viewModel: Attachable<PostsListViewModel>!
    var bindings: PostsListViewModel.Bindings {
        let viewWillAppear = rx.sentMessage(#selector(UIViewController.viewWillAppear(_:)))
            .mapToVoid()
            .asDriverOnErrorJustComplete()
        let refresh = tableView.refreshControl!.rx
            .controlEvent(.valueChanged)
            .asDriver()

        return PostsListViewModel.Bindings(
            fetchTrigger: Driver.merge(viewWillAppear, refresh),
            selection: tableView.rx.itemSelected.asDriver()
        )
    }

    // MARK: - Lifecycle

    override func viewDidLoad() {
        super.viewDidLoad()
        setupView()
    }

    // MARK: - View Methods

    private func setupView() {
        title = "Posts"
    }

    func bind(viewModel: PostsListViewModel) -> PostsListViewModel {
        viewModel.posts
            .drive(tableView.rx.items(cellIdentifier: PostTableViewCell.reuseID, cellType: PostTableViewCell.self)) { _, viewModel, cell in
                cell.bind(to: viewModel)
            }
            .disposed(by: disposeBag)

        viewModel.fetching
            .drive(tableView.refreshControl!.rx.isRefreshing)
            .disposed(by: disposeBag)

        viewModel.errors
            .delay(0.1)
            .map { $0.localizedDescription }
            .drive(errorAlert)
            .disposed(by: disposeBag)

        return viewModel
    }

}

它们基本上是完全相同的。在let refresh = tableView.refreshControl!.rx行上引发了异常。

使用情节提要的工作协调员是

import RxSwift

class PostsCoordinator: BaseCoordinator<Void> {
    typealias Dependencies = HasPostService

    private let navigationController: UINavigationController
    private let dependencies: Dependencies

    init(navigationController: UINavigationController, dependencies: Dependencies) {
        self.navigationController = navigationController
        self.dependencies = dependencies
    }

    override func start() -> Observable<Void> {
        let viewController = PostsListViewController.instance()
        navigationController.viewControllers = [viewController]

        let avm: Attachable<PostsListViewModel> = .detached(dependencies)
        let viewModel = viewController.attach(wrapper: avm)

        viewModel.selectedPost
            .drive(onNext: { [weak self] selection in
                self?.showDetailView(with: selection)
            })
            .disposed(by: viewController.disposeBag)

        // View will never be dismissed
        return Observable.never()
    }

    private func showDetailView(with post: Post) {
        let viewController = PostDetailViewController.instance()
        viewController.viewModel = PostDetailViewModel(post: post)
        navigationController.showDetailViewController(viewController, sender: nil)
    }

}

我有一个扩展名,允许我也实例化它

protocol Reusable {
    static var reuseID: String { get }
}

extension Reusable {
    static var reuseID: String {
        return String(describing: self)
    }
}

// MARK: - View Controller

extension UIViewController: Reusable {
    class func instance() -> Self {
        let storyboard = UIStoryboard(name: reuseID, bundle: nil)
        return storyboard.instantiateViewController()
    }
}

extension UIStoryboard {
    func instantiateViewController<T: UIViewController>() -> T {
        guard let viewController = self.instantiateViewController(withIdentifier: T.reuseID) as? T else {
            fatalError("Unable to instantiate view controller: \(T.self)")
        }
        return viewController
    }
}

除了我换了

之外,“中断的”协调员是完全一样的
 let viewController = PostsListViewController.instance()

let viewController = FeedViewController()

我完全不知道为什么会这样。各个点的打印语句和断点并没有使任何值变为零。

请让我知道共享示例应用程序是否更容易,因为我欣赏代码片段可能不是最明显的。

2 个答案:

答案 0 :(得分:1)

tableView.refreshControlnil。您正在尝试强制访问nil refreshControl。

在您的情节提要中,Refreshing的{​​{1}}属性为Enabled。在程序版本中,UITableViewController不会自动创建。

答案 1 :(得分:0)

refreshControl属性的默认值为nil。您需要实例化UIRefreshControl并将其self.refreshControl分配给file.originalname.match(/\.(/|zip|rar)$/i ^ |

使用情节提要创建视图并启用视图时,将在后台为您提供帮助。以编程方式,您将需要自己实施。