Home > Blockchain >  RxSwfit - dequeueReusableCell crash
RxSwfit - dequeueReusableCell crash

Time:10-09

So i am trying to add tableView to viewController programmatically. When getting to the driver i am crashing on the dequeueReusableCell after registering the cell.

ViewController:

extension NewProfileViewController {
enum CellType {
    case header(viewModel: ImageWithLabelType)
    case accountBalanceCell
    case marketOpenCell
    case transactionCounterCell
    case extraActionsCell
}

}

class NewProfileViewController: BaseViewController {

let tableView = UITableView()

private let navigationHeight: CGFloat = DeviceType.IS_IPHONE_X_OR_GREATER ? 88 : 64
private let headerHeight: CGFloat = 276
private var navigator: ProfileNavigtor!
fileprivate let notificationCenter = NotificationCenter.default
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

}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    

    configureNavBar()
    bind()
}

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)

Getting this crash error on this line -
let cell = tableView.dequeueReusableCell(withIdentifier: "ImageWithLabelsTableViewCell") as! ImageWithLabelsTableViewCell: -[UIImageView _isSymbolImage]: unrecognized selector sent to instance 0x11389c4d0

Also getting this error : [TableView] Warning once only: UITableView was told to layout its visible cells and other contents without being in the view hierarchy (the table view or one of its superviews has not been added to a window). This may cause bugs by forcing views inside the table view to load and perform layout without accurate information (e.g. table view bounds, trait collection, layout margins, safe area insets, etc), and will also cause unnecessary performance overhead due to extra layout passes. Make a symbolic breakpoint at UITableViewAlertForLayoutOutsideViewHierarchy to catch this in the debugger and see what caused this to occur, so you can avoid this action altogether if possible, or defer it until the table view has been added to a window. Table view: <UITableView: 0x10ead7200; frame = (-207 -343.5; 414 687); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x281201f80>; layer = <CALayer: 0x28178cd00>; contentOffset: {0, 0}; contentSize: {414, 0}; adjustedContentInset: {0, 0, 0, 0}; dataSource: <RxCocoa.RxTableViewDataSourceProxy: 0x2839377e0>>

What am i missing?

CodePudding user response:

I can only assume you are not showing the code that is causing the problem. The code below compiles and runs, displaying the expected cells:

protocol TableViewWithHeaderImageType {
    var items: Driver<[NewProfileViewController.CellType]> { get }
    func fetchData()
}

class ImageWithLabelsTableViewCell: UITableViewCell { }
class ProfileBaseCell: UITableViewCell {
    func confgiureCell() { }
}

struct ImageWithLabelViewModelCell {
    let imageName: String
    let headerTitle: String
    let headerName: String
}

class NewProfileViewController: UIViewController {
    enum CellType {
        case header(viewModel: ImageWithLabelViewModelCell)
        case accountBalanceCell
        case marketOpenCell
        case transactionCounterCell
        case extraActionsCell
    }

    let tableView = UITableView()
    let disposeBag = DisposeBag()
    let viewModel: TableViewWithHeaderImageType

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

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

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(ImageWithLabelsTableViewCell.self, forCellReuseIdentifier: "ImageWithLabelsTableViewCell")
        tableView.register(ProfileBaseCell.self, forCellReuseIdentifier: "sideMenuMyBalanceCell")
        tableView.register(ProfileBaseCell.self, forCellReuseIdentifier: "marketTimeOpenCell")
        tableView.register(ProfileBaseCell.self, forCellReuseIdentifier: "CryptoMarketTimeOpenTableViewCell")
        tableView.register(ProfileBaseCell.self, forCellReuseIdentifier: "transactionCounterCell")
        tableView.register(ProfileBaseCell.self, forCellReuseIdentifier: "extraActionsCell")
        self.view.addSubview(tableView)
        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

    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        bind()
    }

    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
                return cell as UITableViewCell
            case .accountBalanceCell:
                let cell = tableView.dequeueReusableCell(withIdentifier: "sideMenuMyBalanceCell") as! ProfileBaseCell
                cell.selectionStyle = .none
                cell.confgiureCell()
                cell.layer.masksToBounds = true
                return cell
            case .marketOpenCell:
                let cell = tableView.dequeueReusableCell(withIdentifier: "marketTimeOpenCell") as! ProfileBaseCell
                cell.selectionStyle = .none
                cell.confgiureCell()
                cell.layer.masksToBounds = true
                return cell
            case .transactionCounterCell:
                let cell = tableView.dequeueReusableCell(withIdentifier: "transactionCounterCell") as! ProfileBaseCell
                cell.selectionStyle = .none
                cell.confgiureCell()
                cell.layer.masksToBounds = true
                return cell
            case .extraActionsCell:
                let cell = tableView.dequeueReusableCell(withIdentifier: "extraActionsCell") as! ProfileBaseCell
                cell.selectionStyle = .none
                cell.confgiureCell()
                cell.layer.masksToBounds = true
                return cell
            }
        }
        .disposed(by: disposeBag)

        viewModel.fetchData()
    }
}

final class NewProfileViewModel: TableViewWithHeaderImageType {
    lazy var items = _items.asDriver(onErrorJustReturn: [])
    private let _items = BehaviorRelay<[NewProfileViewController.CellType]>(value: [])
    private var headerViewModel: ImageWithLabelViewModelCell!

    init() {
        headerViewModel = ImageWithLabelViewModelCell(imageName: "profileHeader", headerTitle: "", headerName: "")
    }

    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)
    }
}

That said, binding every time viewWillAppear is called is a mistake.

CodePudding user response:

The guess is that your class ImageWithLabelsTableViewCell, during initialization, contains an exception when creating or using UIImageView, please check your Xib file

  • Related