RxSwift代码创建UITableView时dequeueReusableCell崩溃问题求助
问题
我尝试通过代码在ViewController中添加UITableView,注册Cell后,在Driver绑定流程中调用dequeueReusableCell时发生崩溃。
崩溃发生在该行:let cell = tableView.dequeueReusableCell(withIdentifier: "ImageWithLabelsTableViewCell") as! ImageWithLabelsTableViewCell
错误信息:-[UIImageView _isSymbolImage]: unrecognized selector sent to instance 0x11389c4d0
同时收到警告:[TableView] Warning once only: UITableView was told to layout its visible cells and other contents without being in the view hierarchy...
ViewController代码
extension NewProfileViewController { enum CellType { case header(viewModel: ImageWithLabelType) case accountBalanceCell case marketOpenCell case transactionCounterCell case extraActionsCell } } class NewProfileViewController: BaseViewController { let tableView = UITableView() let disposeBag = DisposeBag() var viewModel: TableViewWithHeaderImageType private var headerState: HeaderState = .white init(viewModel: TableViewWithHeaderImageType) { self.viewModel = viewModel navigator = viewModel.navigator super.init(nibName: nil, bundle: nil) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() navigator = viewModel.navigator configureTableView() self.view.addSubview(tableView) tableView.backgroundColor = ColorName.white tableView.separatorStyle = .none tableView.translatesAutoresizingMaskIntoConstraints = false tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true bind() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) configureNavBar() } func configureNavBar() { let investImage = UIImage(named: "pepperInvestWhite") let imageView = UIImageView(image: investImage) addCustomTitleView(view: imageView) addLeftNavigationItem(menuItem: .whiteChat) } override func leftNavigationItemClicked(navigationItem: NavigationItem) { // openPepperChat() } private func bind() { viewModel.items.drive(tableView.rx.items) { tableView, index, element in switch element { case .header: let cell = tableView.dequeueReusableCell(withIdentifier: "ImageWithLabelsTableViewCell") as! ImageWithLabelsTableViewCell // cell.selectionStyle = .none return cell case .accountBalanceCell: let cell = tableView.dequeueReusableCell(withIdentifier: "sideMenuMyBalanceCell") as! ProfileBaseCell cell.selectionStyle = .none cell.confgiureCell() //cell.delegate = self cell.layer.masksToBounds = true return cell case .marketOpenCell: let cell = tableView.dequeueReusableCell(withIdentifier: "marketTimeOpenCell") as! ProfileBaseCell cell.selectionStyle = .none cell.confgiureCell() //cell.delegate = self cell.layer.masksToBounds = true return cell case .transactionCounterCell: let cell = tableView.dequeueReusableCell(withIdentifier: "transactionCounterCell") as! ProfileBaseCell cell.selectionStyle = .none cell.confgiureCell() //cell.delegate = self cell.layer.masksToBounds = true return cell case .extraActionsCell: let cell = tableView.dequeueReusableCell(withIdentifier: "extraActionsCell") as! ProfileBaseCell cell.selectionStyle = .none cell.confgiureCell() //cell.delegate = self cell.layer.masksToBounds = true return cell } }.disposed(by: disposeBag) viewModel.fetchData() } private func configureTableView() { tableView.register(UINib(nibName: "ImageWithLabelsTableViewCell", bundle: nil), forCellReuseIdentifier: "ImageWithLabelsTableViewCell") tableView.register(UINib(nibName: "sideMenuMyBalanceCell", bundle: nil), forCellReuseIdentifier: "sideMenuMyBalanceCell") tableView.register(UINib(nibName: "marketTimeOpenCell", bundle: nil), forCellReuseIdentifier: "marketTimeOpenCell") tableView.register(UINib(nibName: "CryptoMarketTimeOpenTableViewCell", bundle: nil), forCellReuseIdentifier: "CryptoMarketTimeOpenTableViewCell") tableView.register(UINib(nibName: "transactionCounterCell", bundle: nil), forCellReuseIdentifier: "transactionCounterCell") tableView.register(UINib(nibName: "extraActionsCell", bundle: nil), forCellReuseIdentifier: "extraActionsCell") tableView.backgroundColor = ColorName.white tableView.separatorStyle = .none } }
ViewModel代码
final class NewProfileViewModel: TableViewWithHeaderImageType { var didPushRightNavigationItem = PublishRelay<Void>() let moneyTransferStatus = BehaviorRelay<MoneyTransferStatus>(value: .noMoney) private var profileEventFactory: ProfileEventFactory var navigator: ProfileNavigtor lazy var items = _items.asDriver(onErrorJustReturn: []) private let _items = BehaviorRelay<[NewProfileViewController.CellType]>(value: []) private var headerViewModel: ImageWithLabelType! init(navigator: ProfileNavigtor, profileEventFactory: ProfileEventFactory) { self.navigator = navigator self.profileEventFactory = profileEventFactory subscribe() } private func subscribe() { let titleAttributedString = NSMutableAttributedString(string: L10n.funYouCameBack, attributes: [NSAttributedString.Key.font: UIFont(name: "Orion-ExtraBold", size: 36)!,NSAttributedString.Key.foregroundColor: ColorName.black]) let name = User.me?.firstNameHE == "" ? User.me?.name : User.me?.firstNameHE ?? "שם" let nameAttributedString = NSMutableAttributedString(string: name ?? "", attributes: [NSAttributedString.Key.font: UIFont(name: "Orion-ExtraBold", size: 36)!,NSAttributedString.Key.foregroundColor: ColorName.white]) headerViewModel = ImageWithLabelViewModelCell(imageName: "profileHeader", headerTitle: titleAttributedString, headerName: nameAttributedString) } func fetchData() { var cellsData = [NewProfileViewController.CellType]() cellsData.append(.header(viewModel: headerViewModel)) cellsData.append(.accountBalanceCell) cellsData.append(.marketOpenCell) cellsData.append(.transactionCounterCell) cellsData.append(.extraActionsCell) _items.accept(cellsData) } }
解决方案
1. 调整TableView的视图层级加入时机
警告信息明确指出UITableView在未加入视图层级时就被要求布局内容,这会触发异常布局逻辑。需要先将TableView添加到视图层级,再执行配置和绑定操作:
修改viewDidLoad方法的顺序:
override func viewDidLoad() { super.viewDidLoad() navigator = viewModel.navigator // 先添加到视图 self.view.addSubview(tableView) // 设置约束 tableView.backgroundColor = ColorName.white tableView.separatorStyle = .none tableView.translatesAutoresizingMaskIntoConstraints = false tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true // 再配置TableView configureTableView() // 最后绑定数据 bind() }
2. 检查ImageWithLabelsTableViewCell的XIB文件
崩溃信息显示向UIImageView发送了它不识别的_isSymbolImage方法,大概率是XIB文件存在问题:
- 确认XIB中根视图是
UITableViewCell,而非UIImageView或其他控件 - 检查Cell的class是否正确设置为
ImageWithLabelsTableViewCell - 验证XIB中所有IBOutlet连接是否正确,没有关联到错误的控件
3. 安全解包Cell(可选但推荐)
强制解包as!会在Cell获取失败时直接崩溃,建议改为可选绑定,增加代码健壮性:
case .header: guard let cell = tableView.dequeueReusableCell(withIdentifier: "ImageWithLabelsTableViewCell") as? ImageWithLabelsTableViewCell else { fatalError("无法获取ImageWithLabelsTableViewCell") // 或返回自定义占位Cell } return cell
内容的提问来源于stack exchange,提问作者ironRoei
相关产品推荐
相关产品推荐

