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