skip to Main Content

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

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)

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?

2

Answers


  1. 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.

    Login or Signup to reply.
  2. The guess is that your class ImageWithLabelsTableViewCell, during initialization, contains an exception when creating or using UIImageView, please check your Xib file

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search